[{"data":1,"prerenderedAt":481},["ShallowReactive",2],{"footer-primary":3,"footer-secondary":93,"footer-description":119,"tv-ready-set-battlesnake":121,"tv-ready-set-battlesnake-seasons":132,"tv-ready-set-battlesnake-episodes":142,"sales-reps":229},{"items":4},[5,29,49,69],{"id":6,"title":7,"url":8,"page":8,"children":9},"522e608a-77b0-4333-820d-d4f44be2ade1","Solutions",null,[10,15,20,25],{"id":11,"title":12,"url":8,"page":13},"fcafe85a-a798-4710-9e7a-776fe413aae5","Headless CMS",{"permalink":14},"/solutions/headless-cms",{"id":16,"title":17,"url":8,"page":18},"79972923-93cf-4777-9e32-5c9b0315fc10","Backend-as-a-Service",{"permalink":19},"/solutions/backend-as-a-service",{"id":21,"title":22,"url":8,"page":23},"0fa8d0c1-7b64-4f6f-939d-d7fdb99fc407","Product Information",{"permalink":24},"/solutions/product-information-management",{"id":26,"title":27,"url":28,"page":8},"63946d54-6052-4780-8ff4-91f5a9931dcc","100+ Things to Build","https://directus.io/blog/100-tools-apps-and-platforms-you-can-build-with-directus",{"id":30,"title":31,"url":8,"page":8,"children":32},"8ab4f9b1-f3e2-44d6-919b-011d91fe072f","Resources",[33,37,41,45],{"id":34,"title":35,"url":36,"page":8},"f951fb84-8777-4b84-9e91-996fe9d25483","Documentation","https://docs.directus.io",{"id":38,"title":39,"url":40,"page":8},"366febc7-a538-4c08-a326-e6204957f1e3","Guides","https://docs.directus.io/guides/",{"id":42,"title":43,"url":44,"page":8},"aeb9128e-1c5f-417f-863c-2449416433cd","Community","https://directus.chat",{"id":46,"title":47,"url":48,"page":8},"da1c2ed8-0a77-49b0-a903-49c56cb07de5","Release Notes","https://github.com/directus/directus/releases",{"id":50,"title":51,"url":8,"page":8,"children":52},"d61fae8c-7502-494a-822f-19ecff3d0256","Support",[53,57,61,65],{"id":54,"title":55,"url":56,"page":8},"8c43c781-7ebd-475f-a931-747e293c0a88","Issue Tracker","https://github.com/directus/directus/issues",{"id":58,"title":59,"url":60,"page":8},"d77bb78e-cf7b-4e01-932a-514414ba49d3","Feature Requests","https://github.com/directus/directus/discussions?discussions_q=is:open+sort:top",{"id":62,"title":63,"url":64,"page":8},"4346be2b-2c53-476e-b53b-becacec626a6","Community Chat","https://discord.com/channels/725371605378924594/741317677397704757",{"id":66,"title":67,"url":68,"page":8},"26c115d2-49f7-4edc-935e-d37d427fb89d","Cloud Dashboard","https://directus.cloud",{"id":70,"title":71,"url":8,"page":8,"children":72},"49141403-4f20-44ac-8453-25ace1265812","Organization",[73,78,84,88],{"id":74,"title":75,"url":76,"page":77},"1f36ea92-8a5e-47c8-914c-9822a8b9538a","About","/about",{"permalink":76},{"id":79,"title":80,"url":81,"page":82},"b84bf525-5471-4b14-a93c-225f6c386005","Careers","#",{"permalink":83},"/careers",{"id":85,"title":86,"url":87,"page":8},"86aabc3a-433d-434b-9efa-ad1d34be0a34","Brand Assets","https://drive.google.com/drive/folders/1lBOTba4RaA5ikqOn8Ewo4RYzD0XcymG9?usp=sharing",{"id":89,"title":90,"url":8,"page":91},"8d2fa1e3-198e-4405-81e1-2ceb858bc237","Contact",{"permalink":92},"/contact",{"items":94},[95,101,107,113],{"id":96,"title":97,"url":8,"page":98,"children":100},"8a1b7bfa-429d-4ffc-a650-2a5fdcf356da","Cloud Policies",{"permalink":99},"/cloud-policies",[],{"id":102,"title":103,"url":81,"page":104,"children":106},"bea848ef-828f-4306-8017-6b00ec5d4a0c","License",{"permalink":105},"/bsl",[],{"id":108,"title":109,"url":81,"page":110,"children":112},"4e914f47-4bee-42b7-b445-3119ee4196ef","Terms",{"permalink":111},"/terms",[],{"id":114,"title":115,"url":81,"page":116,"children":118},"ea69eda6-d317-4981-8421-fcabb1826bfd","Privacy",{"permalink":117},"/privacy",[],{"description":120},"\u003Cp>A composable backend to build your Headless CMS, BaaS, and more.&nbsp;\u003C/p>",{"id":122,"title":123,"logo":124,"cover":125,"tile":126,"announcement_text":8,"description":127,"slug":128,"one_liner":129,"card_text":8,"status":130,"sort":131},"d9e33574-a24f-4f18-b7f5-088b50a63010","Ready Set Battlesnake","8f23d280-2dae-451c-827a-78649c7f059b","9b6b5f23-fc75-4a87-abc5-f549224c3fcb","a9af3e01-33c2-4e06-8836-bcb759570406","Battlesnake is a popular competitive game where code is the controller and only one snake can win. Andrew and Kevin spend their episodes building a capable snake inside of a Directus project. ","ready-set-battlesnake","Join Andrew and Kevin as they build a Battlesnake with Directus.","published",4,[133],{"id":134,"number":135,"show":122,"year":136,"episodes":137},"398d2955-6ca4-4847-810d-a07f4f48a319",1,"2024",[138,139,140,141],"08d92984-9c2a-496d-a748-2ba54e64e378","7c58048d-ab8e-42cc-823d-541848ad4a7d","5a14d9c9-0b37-4655-b45a-b4783c0bb92f","fa776fca-1f88-44ac-8c54-96232474282d",[143,166,186,206],{"id":138,"slug":144,"vimeo_id":145,"description":146,"tile":147,"length":148,"resources":8,"people":149,"episode_number":135,"published":156,"title":157,"video_transcript_html":158,"video_transcript_text":159,"content":8,"seo":8,"status":130,"episode_people":160,"recommendations":163,"season":164},"start","925179495","In the first episode of the season, Kevin and Andrew bring you on the journey of learning about Battlesnake by building an (arguably) capable autonomous snake based on Node.js. ","8b37f7c7-31a2-4f7f-9293-1b8955f77ddb",111,[150,153],{"name":151,"url":152},"Kevin Lewis","https://directus.io/team/kevin-lewis",{"name":154,"url":155},"Andrew MacLean","https://twitter.com/andrewdmaclean","2024-03-21","Start","\u003Cp>Speaker 0: Hello, everyone, and welcome to Ready, Set, Battlesnake. I am so thrilled for this season to be happening, bringing together a few of my interests all at the same time, including hanging out with my good friend, Andrew. Hello, Andrew.\u003C/p>\u003Cp>Speaker 1: Hey, Kevin. How are you?\u003C/p>\u003Cp>Speaker 0: I am all the better for seeing you. Would you like to tell us what the hell we're gonna be doing for the next 4 episodes?\u003C/p>\u003Cp>Speaker 1: Yeah. That's a great question. So we are gonna be exploring the wonderful world of Battlesnake. More on that in just a minute. We're gonna be looking at how Battlesnake works.\u003C/p>\u003Cp>We're gonna be looking at how Battlesnake integrates with some other really cool technologies out there. And, hopefully, by the end of this 4 week series, you'll have a Battlesnake or 4 episode series. You'll have a Battlesnake that is ready to go and to jump in and start, yeah, start competing and playing around and learning some new technologies.\u003C/p>\u003Cp>Speaker 0: Or you can just watch us fumbling around. That's also totally fine too. Also very No. I think what might be good is let's talk about what Battlesnake is and then talk about, like, our affiliations with Battlesnake. Probably feels like a good thing to throw out there.\u003C/p>\u003Cp>But let's start because we've said the phrase Battlesnake one too many times to not jump into what it is. What is Battlesnake, Andrew?\u003C/p>\u003Cp>Speaker 1: Alright. So if you have ever heard of the old classic game Snake, like that old Nokia snake game where you, sort of control the snake to go around on a screen and eat little berries and get longer, and you try to avoid yourself and avoid the walls and avoid other snakes if they're on the board. Well, Battlesnake is that same idea, but moved around a little bit into the developer space. So it's a really cool way for developers to explore new technologies, new frameworks in this kind of technology agnostic way. So you actually, create a web server that responds to the Battlesnake API, and you can crawl control a snake using code to battle or compete against other snakes in that kind of same gamified way, but, in kind of way more fun for developers.\u003C/p>\u003Cp>Speaker 0: And a few interesting notes here. So firstly, unlike snake on your Nokia, multiple snakes on the board at once, and you're basically trying to outlive everyone else. You die in the same ways, but there are multiple snakes just like this on the screen now. And because what you're doing is implementing an API, it means it's pretty technology agnostic as long as you can host a web server that can have a couple of specific endpoints that accept requests and then responds in a specific way. It almost doesn't matter what technology you use, which is pretty cool.\u003C/p>\u003Cp>And so we're going to be building this over a few episodes. Today, we're just gonna be focusing on, like, the vanilla Node. Js starter snake and challenges so we can introduce you to this wonderful world. You're gonna love it. And then we'll play around with, like, running Battlesnake in a director's project with directors automate, which should be really fun.\u003C/p>\u003Cp>And then, towards the end, we're going to also integrate dev cycle, which brings me on to a probably good point here, affiliations. Andrew, do you wanna start with, like, your history with this project?\u003C/p>\u003Cp>Speaker 1: Yeah. Sure. So so I actually started with Battlesink back in 2020. So I joined fairly early on in in the the official Battlesink company. BattleState had been around for about 5 years before I joined the team, and it started out as a community project here on the West Coast of Canada.\u003C/p>\u003Cp>It was a big sort of hackathon event that happened here where a bunch of people have gotten to a room for a couple of days and built battle stakes and competed in sort of live events. I joined the team in 2020 as sort of their first community manager as we were kind of transitioning, as many were in 2020, into that online world. And so we sort of transitioned the whole kind of BattleState concept into an online platform that folks could engage with around the world. In 2023, Battlesnake, was acquired by another Canadian startup called Dev Cycle, where I currently work as their developer advocate. And so I still get to play around with Battlesnake a lot and work with a lot of the great people that were on the Battlesnake team.\u003C/p>\u003Cp>And and it's actually like the the pre dev cycle times that, Kevin, you and I met, even though technically, we never met in person until Not\u003C/p>\u003Cp>Speaker 0: in person yet. Mhmm. Absolutely. And then, I, Battlesnake as part of going online, run a bunch of livestream series. It was a really good is still, but was really then in its peak, like, peak pandemic.\u003C/p>\u003Cp>We're doing everything online, ton of live stream series, including the competitive leagues, shows which discuss different algorithms that can be implemented. And this one show called Coding Badly, which was run with me and my friend, Joe. And this is kind of a spiritual successor to Coding Badly, I think. No offense, Andrew. I think that's probably gonna be the vibe the vibe we go for.\u003C/p>\u003Cp>So I love Battlesnake. And what's really cool about and we'll discover throughout today is at the very core, you know, there are some very simple rules. Don't hit into walls, don't hit yourself, Don't hit other snakes. Don't run out of life like it. Get food when you need it.\u003C/p>\u003Cp>But once you start to get options, you can really make some choices here. You know, you can say if left and right are both viable options, is there an algorithmically, beneficial way based on certain strategies to go? Do I box myself off, but now we only have so much life? Do I try and find food as quickly as possible at all cost to also starve out other snakes and so on? So all of this is implemented in code, and then when the games actually happen, your hands off.\u003C/p>\u003Cp>You know, your your algorithm takes over and makes decisions. So, yeah, I think that's that I I think that's most of Battlesnake. Any other thoughts, Andrew, before we crack on?\u003C/p>\u003Cp>Speaker 1: I mean, let's, I mean, let's jump into the let's jump into the docs because I think they're gonna give us a good idea of kind of the some of the things that maybe we missed. And I think, actually, I'm I'm not a 100% sure. I haven't been in the docs in a while. I may still be featured in, like, the what the heck is Battlesnake doc. So I'm very interested to see if we\u003C/p>\u003Cp>Speaker 0: can get it. Shortly.\u003C/p>\u003Cp>Speaker 1: Am I there? The real question. I'm not oh, wait. No. Hold on.\u003C/p>\u003Cp>Quick start? There I am. I'm definitely I'm definitely in the quick start. I know this. Am I still here?\u003C/p>\u003Cp>Speaker 0: We'll we'll throw this video into the resources that accompany this video on Directus TV if you wanna go and watch it in full. Awesome. Yeah. So when you click on that,\u003C/p>\u003Cp>Speaker 1: what is dev cycle, it brings you over to the dev cycle documentation, and this documentation is fantastic. One of the things that Kevin was talking about, a lot of those sort of specific things that you can and can't do, if you wanna kinda figure out what that's all about, you can really a great place to go is actually checking out the game rules. And so this is where we start to look at things like this collision idea. So, like, self collisions and body collisions and head to head and who wins in these different situations, and then also giving information about some other things that are really important in this kind of gameplay. So you have a starting position in the game.\u003C/p>\u003Cp>We'll talk a little bit more about what that looks like kind of as we go through. You have health. That's a dynamic in this game as well. There's food spawns and hazards, and we're not going to get to a lot of that stuff necessarily today. We're gonna see that kind of as we go on.\u003C/p>\u003Cp>Yeah. But checking out this kind of game rule section is a great way to, like, get a a solid understanding of the idiosyncrasies, I guess, that exist in Battlesnake over sort of the, the the traditional snake game. The other great thing is this rules repository. So, like, all of these rules are actually in, an awesome GitHub repository called Battlesnake Rules. And so if you would rather not watch a video or if you'd rather just do, like, a different kind of deep dive, you can actually, like, jump into the rules repository, which is public, and you can go through and kinda discover a lot of these things yourself.\u003C/p>\u003Cp>It's all built out in Go. So if you're not a Go developer, then maybe not for you, but it's a useful place to kinda check out all of this written in code.\u003C/p>\u003Cp>Speaker 0: And, yeah, and that was gonna be my clarifying point. This isn't pros. This isn't written out rules. But this is codified rules that actually run within the Battlesnake engine. So you can get right in there and understand again the intricacies of how it all works, which is really cool.\u003C/p>\u003Cp>I remember possibly in a coding badly episode, definitely in a battlesnake stream in the past generating, battlesnakes that that, like, actively hook into this rule logic very directly, something like that. Okay. So how do we get started? I think we're gonna stick with node. Doesn't matter what language you you use in the real, but, you know, given that directives is all JavaScript view node, and that's at least what I know.\u003C/p>\u003Cp>That feels like a good place to start.\u003C/p>\u003Cp>Speaker 1: Yeah. I'm with you. 100%. So couple places you can go. Obviously, the quick start that is here.\u003C/p>\u003Cp>So, basically, anywhere you go in the documentation, you'll find the quick start. This one is set up in Python. So, like, if you watch the video and you've never programmed in Python, it's still kind of valuable, and this will kind of walk you through the game logic. But it also does, in this kind of quick start area, let you choose from different snakes. So I highly recommend if you're just getting started with Battlesnake, click on that quick start, and you'll get the kind of fundamental understanding here.\u003C/p>\u003Cp>So when we say you are building a web server server that is kind of serving as your battle snake and it's it's interacting with the battle snake API, this is exactly what's actually happening here in this sort of representation here. So, the game engine is querying your web server where your battlesnake is, and your battlesnake server is returning a response. That response can only be 1 of 4 things, up, down, left, or right. That's right. Right.\u003C/p>\u003Cp>That you received from the game engine, and then you just are kinda repeating this process over and over and over again. So game engine is sending something to your server. Your server is sending something back to the game engine over and over and over again. And, I guess one of the the kind of gotchas here is, the web servers only have 500 milliseconds to respond. And so if you are sort of some people run these on Raspberry Pis and some people or some people run them on you still run them on Heroku servers.\u003C/p>\u003Cp>And so one of the things that we would tell people is make sure you go and, like, do something on your Heroku server or your\u003C/p>\u003Cp>Speaker 0: Just to see how the code stop at the beginning.\u003C/p>\u003Cp>Speaker 1: Yeah. Exactly. But, but yeah. And and, Kevin, I don't know if I ever told you. Do you know what are the reasons why this 500 millisecond rule was implemented?\u003C/p>\u003Cp>Have you ever heard this story?\u003C/p>\u003Cp>Speaker 0: No. Sorry. I just realized. I don't think you can see me. So I I'm doing a No.\u003C/p>\u003Cp>Speaker 1: I can. No. No.\u003C/p>\u003Cp>Speaker 0: I can\u003C/p>\u003Cp>Speaker 1: see that. I'm, like, down here. No. I can't. So so I, so in one of the early kind of days of the battle sync competition, before they had this 500 millisecond, like, response time that was there, somebody realized that it was actually faster to respond with a physical keyboard or a game controller than it was to send code back.\u003C/p>\u003Cp>Love. And so somebody actually went and built a Battlesnake that was hand controlled, and then they would respond in real time. And it's interesting because I think they did fairly well. And at the end of it, it wasn't like, no. You did the wrong thing.\u003C/p>\u003Cp>It was like, great. You kind of you broke the rule, and now we or or or, like, defined the new rule for us. And so\u003C/p>\u003Cp>Speaker 0: That's funny.\u003C/p>\u003Cp>Speaker 1: I think you could technically still do it, but, you'd have to have some pretty fast thumbs.\u003C/p>\u003Cp>Speaker 0: In planning for this series, not to spoil it, but we have spoken about what it might mean to change strategy, like, mid game. You know, there are there are ways we can achieve this. It was not real time. That's really funny. And the interesting thing about that limit is it really forces you to be computationally efficient as you start to build more complex things.\u003C/p>\u003Cp>At the beginning, literally, it doesn't matter. You're as long as your server can physically respond in that time, you know, it it doesn't matter. But what you can do with things like at the beginning of a game, spin up some kind of machine learning or artificial intelligence resource, and then per move, you can call on it, but you have to do it very efficiently. So are we gonna start with a starter snake, or are we gonna start from 0?\u003C/p>\u003Cp>Speaker 1: No. No. I think we're I think I think let's start with a starter snake because that's what I think most people are gonna do here. Perfect. And so we'll go to kind of the first step.\u003C/p>\u003Cp>We're gonna choose our starter project. I'll just open this up in a new tab here. And what I kinda love here is, and I was talking to Kevin before the the show started, is like, so I'm running on a Chromebook right now. So if you're sort of a newer software developer, and you're, like, just getting exposed to this world, and you're, like, I don't have a device that can run this. If you can run a Chrome browser, you can run this.\u003C/p>\u003Cp>Speaker 0: Absolutely.\u003C/p>\u003Cp>Speaker 1: I mean, if you wanna have, like, a crazy setup, like, some people have, like, stand alone servers that run their state, You totally can, but you definitely do not need to do that to kinda get started with this.\u003C/p>\u003Cp>Speaker 0: No. Yeah. Not at all.\u003C/p>\u003Cp>Speaker 1: And so what's also great here too is so you can see these are the official starter stakes that are up on the screen. Yes. They are up on the screen. I'm gonna zoom in a little bit. I'm gonna try and zoom in a little bit.\u003C/p>\u003Cp>I'm so used to, like, the Mac keys, so I always click the I've used a Windows keyboard on a Mac device, so I'm always hitting the Windows key. Anyways, so You can\u003C/p>\u003Cp>Speaker 0: you can remap them. That's what I had to do. I have a German keyboard on my physical MacBook. I'll just remap the keys in it. Just a few keys on as they're printed, but you just get used to it.\u003C/p>\u003Cp>Oh, I like that.\u003C/p>\u003Cp>Speaker 1: I should do it. I feel like I've tried it before and it just didn't work. Anyways, not important. So, 5 starter snakes you can start with, which are, like, officially supported starter snakes. So Python, Go, Rust, TypeScript, and then the last one there, JavaScript is what we're gonna look at today.\u003C/p>\u003Cp>But if you're like, I really wanna do an f sharp snake, there is an f sharp snake for you there. And there's some really cool ones that have been defined in, like, some really, like, interesting languages and setups. Totally worth checking out. But today, we're gonna jump into the JavaScript official snake.\u003C/p>\u003Cp>Speaker 0: Lovely.\u003C/p>\u003Cp>Speaker 1: And what's awesome about these is so you can run these on your own infrastructure. So these are all templates that are set up on there. So you're welcome to run them on there. But we do have these set up ideally if everything works out still. I have not run one of these starter stakes in a while.\u003C/p>\u003Cp>Speaker 0: That makes sense. See if\u003C/p>\u003Cp>Speaker 1: everything works well, is you can just run on Replit. It's just a really simple Node. Js and express server, and, like, there's Docker files for external things, but everything runs right out of the box if everything goes according to plan, which it almost never does. So shall we click the button to show you what?\u003C/p>\u003Cp>Speaker 0: Why would you say that? Now it's all gonna go pear shaped.\u003C/p>\u003Cp>Speaker 1: I know. You say that. No. See, now it goes perfect. You gotta call it out.\u003C/p>\u003Cp>Right? You gotta call out to that\u003C/p>\u003Cp>Speaker 0: You gotta manifest it. And this is gonna be we're gonna nail it. There's not gonna be any any any errors. And I'll Well Battle Snake's gonna win the league.\u003C/p>\u003Cp>Speaker 1: What's that saying, though? It's like break a leg. Right? You say break a leg before they go on the stage because you're, like, saying the bad thing that might happen. I feel like it's the same thing here.\u003C/p>\u003Cp>If you just say, like, this is not gonna work at all, it works perfectly.\u003C/p>\u003Cp>Speaker 0: Allegedly. Let's find out. We're gonna see what happens.\u003C/p>\u003Cp>Speaker 1: Alright. So we're gonna click here. We'll see if I'm logged in. I have a feeling that I'm not.\u003C/p>\u003Cp>Speaker 0: You're not.\u003C/p>\u003Cp>Speaker 1: I'm not. But I don't think it really mat I think it'll still spin up if I'm not logged in. We'll find out.\u003C/p>\u003Cp>Speaker 0: We'll see\u003C/p>\u003Cp>Speaker 1: what happens. Nope. I have to log in. That's all good. We've follow look at that.\u003C/p>\u003Cp>I didn't even know we had followers right now.\u003C/p>\u003Cp>Speaker 0: Alright. I'm gonna close you\u003C/p>\u003Cp>Speaker 1: because we don't need you right now. I'm gonna close you. Yeah.\u003C/p>\u003Cp>Speaker 0: And then when we when we get into REPL, it will bump the font size up significantly, I think. Yeah.\u003C/p>\u003Cp>Speaker 1: So, yeah, it's this easy. So we're literally just clicking on that fork. We're gonna do an auth zero sign in. Hopefully, we can skip past the stage where I'm gonna put my password. Let's go.\u003C/p>\u003Cp>I think Toby Flender snake is still on Replit 2, which is my which is my battle snake.\u003C/p>\u003Cp>Speaker 0: Oh. This is what this is what\u003C/p>\u003Cp>Speaker 1: I knew was gonna happen.\u003C/p>\u003Cp>Speaker 0: Alright. But you're log but you're wait. You're not logged in. It it hasn't logged you in.\u003C/p>\u003Cp>Speaker 1: Oh, weird. That's weird. Alright. Let's log in again. Let's go log in.\u003C/p>\u003Cp>Oh, I love it. All the errors, even at the authentication stage, we're running. This does not bode well. Oh, you know what it is? I think it's because I'm like, I need to auth with GitHub.\u003C/p>\u003Cp>That's probably what it is. I do have it on that email, but I'm not auth with GitHub. So let's go do that.\u003C/p>\u003Cp>Speaker 0: Oh, I hate that sometimes when you log in with, like, an SSO provider, but your email address is the same with that SSO provider and you're like, I don't I don't know which which system. Did it work this time? There we go. Okay.\u003C/p>\u003Cp>Speaker 1: Okay. So let's go. So we're in here, and I probably got a bunch of snakes in here. We'll go back over, and we'll jump into our starter projects again. We'll jump back into our JavaScript snake.\u003C/p>\u003Cp>And so I've logged in now. Don't need to worry about billing, but this should be a much more straightforward process. I love I said everything is gonna go incredibly terribly. And even at the auth stage, I definitely have jinxed this.\u003C/p>\u003Cp>Speaker 0: I'm not saying I told you so, but I'm certainly thinking it.\u003C/p>\u003Cp>Speaker 1: Alright. So here we go.\u003C/p>\u003Cp>Speaker 0: So this is the this is\u003C/p>\u003Cp>Speaker 1: the snake. I mean, let's get it in the template right now. Yeah. What do we wanna call this snake?\u003C/p>\u003Cp>Speaker 0: We're gonna call this when you call it something does does the dev cycle mascot have a name? His name's ToggleBot. So I don't know. Do we\u003C/p>\u003Cp>Speaker 1: call it, like, we could call it ToggleBot. We could also call it, like, coding badly rebirth. We could call it, we could call it, let's\u003C/p>\u003Cp>Speaker 0: call it let's call it ready, set, battlesnake.\u003C/p>\u003Cp>Speaker 1: Ready, set, battlesnake. Alright. Let's see.\u003C/p>\u003Cp>Speaker 0: I was worried you're gonna be, like, does the does the rabbit have a name? Does the director's rabbit have a name? As far as I'm aware, it doesn't. So set myself in a losing battle there. Let's use the template.\u003C/p>\u003Cp>Speaker 1: I'm very disappointed. To you.\u003C/p>\u003Cp>Speaker 0: Could be right. Could be right.\u003C/p>\u003Cp>Speaker 1: Here we go. So it's forking. If you've never used Replit before, by the way, amazing cloud based IDE, super powerful, integrates with all of the things that you're already using, but also has a lot of inbuilt features. If you've used things like so if you've used Gitpod, which is, or Codespaces, which are sort of in\u003C/p>\u003Cp>Speaker 0: the Code sandbox is another. Code sandbox.\u003C/p>\u003Cp>Speaker 1: Yes. Code sandbox. I find, somebody described it to me recently though that Replit is kind of like the Google Docs or, like, Google Suite of, like, cloud IDEs. It's just, like, very accessible. Mhmm.\u003C/p>\u003Cp>Speaker 0: Yeah. Bump your font size up. Let let's let's make that text more accessible because I can't read the damn thing.\u003C/p>\u003Cp>Speaker 1: Alright. And I already broke stuff. There we go. Let's zoom in. Wonderful.\u003C/p>\u003Cp>Alright. So let's look at the files that we have here before we start messing around with anything. So it immediately brings us into our index dotjs file. The only other two things that we have in here, ignoring, like, our our package and all the rest\u003C/p>\u003Cp>Speaker 0: of that stuff,\u003C/p>\u003Cp>Speaker 1: are our readme, which is gonna kinda give you, like, things to do, which we're gonna be looking at in that sort of quick start. And then we've got our server dotjs, And this is basically, doing what we described earlier with those different responses that you can do to the Battlesnake API. So there is the just sort of, like, base level. And what what do you call that? Like, the standard with nothing else out of it?\u003C/p>\u003Cp>Speaker 0: The site.\u003C/p>\u003Cp>Speaker 1: There you go. No. No. I mean, like, the the actual just like a slash. Like, no additional\u003C/p>\u003Cp>Speaker 0: Oh, sorry. Sorry. You're referring to that. I would call that the root route.\u003C/p>\u003Cp>Speaker 1: Boom. Root route. So, like, your root route, and we'll talk about kind of what all these things do in a minute. And then you've got your start post route. You've got your move, your end, and then we've got a bunch of other little stuff here, including a console log that actually says your battle tank is running.\u003C/p>\u003Cp>Speaker 0: And if I may just kind of embellish this a little bit. So this main endpoint that we care about is this move endpoint. That is the endpoint that will receive board states and ultimately has to respond with a direction. You'll notice there are these other ones. So this route endpoint handler, route handler there, that will respond with information about your snake.\u003C/p>\u003Cp>We'll do that in due course. But the start and end are interesting. We mentioned earlier about provisioning resource. That's basically where you would do that. Right?\u003C/p>\u003Cp>Because I think you get a small grace period between the start endpoint being hit and the first time the move endpoint is hit in any given game. And then your snake can be entered into many games.\u003C/p>\u003Cp>Speaker 1: Yep. Yep. Absolutely. And then yeah. There there's a bunch of stuff.\u003C/p>\u003Cp>We'll we'll kinda look at some of these and see what they do, but but but Kevin's absolutely right. Like, move is really what matters, and that's where you need to return sort of up down left or right. So the other thing just to look in here, especially for the snake, is just kinda looking at these, like, what you have in this basic package dot JSON file. There's really nothing there. This is a super\u003C/p>\u003Cp>Speaker 0: I think.\u003C/p>\u003Cp>Speaker 1: Super, super light template. And you can go crazy with this, but this is a very, very light template.\u003C/p>\u003Cp>Speaker 0: Mhmm.\u003C/p>\u003Cp>Speaker 1: Okay. So, do we wanna do we wanna run this or should we look through first or should we head back over to the quick start? What do you think, Kevin?\u003C/p>\u003Cp>Speaker 0: I think I think we could do a little less with the quick start because you and I have both like, we should I think we should refer to it as and when it's needed. I say let's get the let's go to the Battlesnake platform. Let's take this basic snake, which I think what what if you scroll down this file, what does the move endpoint do at the moment? Oh, there is some there is some logic there. Yeah.\u003C/p>\u003Cp>We'll talk we'll talk a little bit about that, but it does do stuff. So let's let's set up this snake and get one game running so we can kinda get to grips with how that code becomes a battle snake in a game. I think that that feels about\u003C/p>\u003Cp>Speaker 1: Kevin, I have one question for you, though. What do you think I mean, Replen is trying to tell us to do this desperately, and we haven't done it yet. What is the thing that we need to do?\u003C/p>\u003Cp>Speaker 0: Oh, I wonder if there's one call to action on this page that is significantly brighter than the oh, okay. Alright. Yeah. There was no mystery. We're hitting run.\u003C/p>\u003Cp>Speaker 1: Awesome. And so this is the piece here. So this is the piece that tells you that your Battlesnake is running, is you're gonna see this API response here. So you're gonna see this API version, the author, color, head, tail. And then in your console, if it's running, you're gonna see this running battle snake at whatever the server, domain is and, or server address is and then info.\u003C/p>\u003Cp>Now because of the nature of Battlesnake, you need to have a public, URL that you can share with the platform. This is new. They used to all be public and has since shifted. So I think we need to deploy our app. No.\u003C/p>\u003Cp>We don't need to deploy our app. So we're gonna see what happens. We're gonna see if we can use just this. Yeah. Let's go to new tab.\u003C/p>\u003Cp>And then I yeah. So it'll be like this, and let me bump this up as well. We'll see how much it bumps up actually. There we go. So this is the same thing that we're seeing here.\u003C/p>\u003Cp>And then in our, in our navigation bar, we have this, like, replit.dev link, and so that's what we're gonna put over into the Battlesleep platform. It used to show up right here. I don't know why it doesn't anymore. I wonder if it should\u003C/p>\u003Cp>Speaker 0: I imagine I imagine it got a bit unwieldy as you build more complex URLs\u003C/p>\u003Cp>Speaker 1: by that. That's right. That must be it.\u003C/p>\u003Cp>Speaker 0: And that's probably more useful as a developer just to see the the path, but who knows?\u003C/p>\u003Cp>Speaker 1: You know what, though? I have a feeling with a lot of these platforms, it might also be. Yeah. You know what? So the what you can do is if you just, like, right click on this, like, 0.0.0.08 1,000, similar to other platforms, it'll actually convert it to whatever that public URL is.\u003C/p>\u003Cp>Speaker 0: Oh, that's interesting. So it\u003C/p>\u003Cp>Speaker 1: can actually just copy that over there. Okay. So that's working. So let's head back over to play.battlesnake.com.\u003C/p>\u003Cp>Speaker 0: I'm so excited. Play. Play. Tailwind is a very nice little very nice little playground.\u003C/p>\u003Cp>Speaker 1: It is a very nice playground. This is like this Chromebook has become my central development, tool. And so, like, I have so many, like, interesting things on here. I never thought a Chromebook would be my daily driver. Alright.\u003C/p>\u003Cp>So over on the battle 6 site, auth with GitHub, highly recommend. Not that you need to, but, we're gonna auth there. Oh, look at that. Inclusive waffle. Good.\u003C/p>\u003Cp>Okay. So I'm logged in with my personal account. So here's what I love. I used to so I'm a teacher. If you didn't know that, what you\u003C/p>\u003Cp>Speaker 0: Oh, really? You have absolutely no mannerisms that would give that away. Like, what should we press on the screen now? Is it the green button?\u003C/p>\u003Cp>Speaker 1: But I I I introduced, my, like, middle schoolers to, to Battlesnake a couple of years ago. And, like, all their snakes are still in here, which I kind\u003C/p>\u003Cp>Speaker 0: of That's fun. Some of\u003C/p>\u003Cp>Speaker 1: these are That's Toby still mine, though, runs on node, on Node\u003C/p>\u003Cp>Speaker 0: RED. On Node RED.\u003C/p>\u003Cp>Speaker 1: That is a stellar platform, if you are looking at, having a stake. And actually, funny enough now now that I think about doing a directus snake\u003C/p>\u003Cp>Speaker 0: I was just about to say it. Yeah. Yeah. So so we have, directors automate as part of that. We have flows, which is a low code automation builder.\u003C/p>\u003Cp>And, yeah, if I whenever I actually describe it to developers, I ask if they've seen Node RED because I think it's very comparable. You know, you have automation builders that are very much like Zapier, and then you have ones which are more like Node RED. And this is more like that where you're wiring together these these these operations. But, yeah, we'll get to that.\u003C/p>\u003Cp>Speaker 1: I love it. Alright. So, we gotta add a snake in here. Now if you're new in your account, I think it prompts you where to add your new snake. I haven't done this in a little bit, so we're gonna see.\u003C/p>\u003Cp>I have a feeling\u003C/p>\u003Cp>Speaker 0: I actually need to go into my battlesnakes.\u003C/p>\u003Cp>Speaker 1: Yeah. We're gonna find out. There we go. And here we go. So we got all of our snakes that are currently in here.\u003C/p>\u003Cp>We're gonna create a new one. We're gonna call this one ready, set, Battlesnake. We're gonna drop our server URL in there that we copied over from do we still have it? Yeah. There it is.\u003C/p>\u003Cp>I think that's\u003C/p>\u003Cp>Speaker 0: And specifically without anything after the slash. We have to increment those, but that's correct. Yeah. So this is what\u003C/p>\u003Cp>Speaker 1: you're gonna see, something that looks a little bit like this. If you wanna try and copy this and use my snake, go nuts, or use our snake. And then the next thing you choose here is your engine region. And so I highly recommend that when you're getting started, you don't worry too much about which engine region that you're using. There's a good, like, kind of breakdown on why you would use a different engine region.\u003C/p>\u003Cp>I'm based in in in North America, so and I'm kinda closer to Oregon, so I'm gonna set the state there. Kevin might wanna set it up based in the Netherlands, but it really, really doesn't matter at\u003C/p>\u003Cp>Speaker 0: kind of the base level of Battlesnake. It it matters when you're squeezing out those milliseconds worth of worth of performance, but we're simply not.\u003C/p>\u003Cp>Speaker 1: Exactly. And then, again, description you can add, same with programming language. This is just fun. It gives it a little bit of flair. So you can add a JavaScript, and then we can say this is\u003C/p>\u003Cp>Speaker 0: Platform. We need to we need to do some work here. By the next time we record, we're gonna be doing some work to this list because we'll be we'll be moving we'll be moving our snake over.\u003C/p>\u003Cp>Speaker 1: I love it. Alright. So let's go. And what are we gonna find? What are we hosting on right now?\u003C/p>\u003Cp>We're hosting on Replit, so we should have Replit there. And what I love about this list too is you can actually see, like, here are all the different places that you could host a snake. Now some of these I highly would not recommend. Like, get out Codespaces. I don't think you can actually keep a long running server going.\u003C/p>\u003Cp>But Interesting.\u003C/p>\u003Cp>Speaker 0: No. Yeah. Yeah. You have to have the Codespace, and then they go to sleep.\u003C/p>\u003Cp>Speaker 1: Yeah. But lots of different cool options here. And then the other thing you can do is you can set it so that other people can interact with your snake. And so the upside here is that you can allow other people to kind of play around with it. The downside is you can have people that decide to challenge while you're using your snake for some other thing, and then you're having to deal with all the compute costs.\u003C/p>\u003Cp>And if you're running on, like, AWS or something that charges you for all of those compute hours, definitely challenging.\u003C/p>\u003Cp>Speaker 0: It's not just that. It's every request you get in relation to a single game has an ID of that game. But at basic levels, you're unlikely to be segmenting game logic. And if you're doing, you know, basic battle snakes, we'll just look at a single board and make a single response based on the heuristics of your code, the algorithm you create. But if you're doing something a little more clever way, you're looking over multiple turns, it screws you.\u003C/p>\u003Cp>But by that point, I suppose you are you are using that ID, but that's worth considering as well.\u003C/p>\u003Cp>Speaker 1: Yeah. Absolutely. Okay. So let's jump in here. So we'll leave it as private.\u003C/p>\u003Cp>We'll save this battle snake. So you'll know once you save right away if your battle is actually there because it'll show up with the name, obviously, that you set it here. If you don't, though, you won't get any response here in this ping. So you might put, it used to be that if you started a snake on a service, it wouldn't automatically go. And so you might have to run this ping just to kinda see.\u003C/p>\u003Cp>And you can see here, if we do this ping, we were talking about that 500 millisecond response time. You can see that Replit is really up their, like, their timing. It used to be\u003C/p>\u003Cp>Speaker 0: They didn't. They didn't. That first response was a few hundred. So that must be like a cold start kind of like now it's middling around 40, 50, 60, which is great, but it was multiple 100 the first time you, the first time it was there before you hit ping again out of interest.\u003C/p>\u003Cp>Speaker 1: Yeah. That's true.\u003C/p>\u003Cp>Speaker 0: So But then that's what you're starting to see.\u003C/p>\u003Cp>Speaker 1: A good idea. Yeah. Yeah. Okay. So we've got we've got ready set battlesync in there.\u003C/p>\u003Cp>You'll also see you kinda get the flare that's related to the the language and and sort of hosting service that you're using. And so now, our Battlesnake is in there and is ready to go. So, do we wanna make our Battlesnake look good before we bring it into the game or do we wanna bring it into the game?\u003C/p>\u003Cp>Speaker 0: Let let's jump into it and maybe we'll see if the the Battlesnake elders allow this. We might see if we can do some custom customization during the the act of us recording 4 episodes. But for now, let's just leave our our snake a slightly boring gray. We can come back to it. When when we come and update the platform to direct us, that's where we'll do the customization.\u003C/p>\u003Cp>That'll be next time.\u003C/p>\u003Cp>Speaker 1: I love it. Alright. So, we're gonna go in here. You can choose. So, well, in the traditional Battlesnake game, or traditional snake game, I guess, it's kind of a standard map.\u003C/p>\u003Cp>No hazards. It's kind of just food pops up and things like that. So we're going to choose just the standard mode and a standard board size of 11 by 11. And we're just going to add ready, set, Battlesnake onto the board. We're not going to add any other snakes in there.\u003C/p>\u003Cp>You can add some bots. These bots are always running that have, like, some set sort of modes, but we're just looking to see if our snake is ready to go. So let's go ahead and see, how ready set Battlesnake runs. Alright. We're in.\u003C/p>\u003Cp>Okay. We're here. Should we have it now? You have to.\u003C/p>\u003Cp>Speaker 0: Oh, yeah. Yeah. Come on. Let's do that. You ready?\u003C/p>\u003Cp>I'm so thrilled. No. I'm good. Alright. You ready?\u003C/p>\u003Cp>Yeah. Ready. Oh, no. No. We have a we we we get a catchphrase for this show.\u003C/p>\u003Cp>What? If Reggie said that Oh, my goodness.\u003C/p>\u003Cp>Speaker 1: Yes. Okay. Here we go. Okay. Of course.\u003C/p>\u003Cp>Alright. Let's go. Ready. Ready. Set.\u003C/p>\u003Cp>Battle snake.\u003C/p>\u003Cp>Speaker 0: That was thoroughly underwhelming. I'm gonna be honest. But but we are watching the snake. We got So this is what we got. So you see the ping?\u003C/p>\u003Cp>You saw the ping going on the right. So it was still around that 40 millisecond mark. There were 47 turns that happened before our snake died. Why did it die? And the nice thing about this editor, this player, of course, is you can step through your turns.\u003C/p>\u003Cp>So it was performing kind of a slightly eclectic set of moves, which is great. But what eventually happened to it? So it was at the bottom and it went off the board. It went down. So that was that's that's what our snake did.\u003C/p>\u003Cp>Now just to be clear, we'll look into the logic of our battle snake next, but we don't quite know why let's look at our code. And now we're at the point where we can start customizing this, this battle snake.\u003C/p>\u003Cp>Speaker 1: Alright. So we're gonna jump back over to Remplit. And so all of the things that you need to know for this snake when you're getting started are all gonna live here. And the other thing that's really cool here that I guess we didn't talk about earlier and maybe you didn't see here because we didn't go that far down in the code is we're actually returning into the console every move. So you can see why it moved or not why it moved in that direction, but you can see what happened.\u003C/p>\u003Cp>So this in this game start move 0, move 1, all of these commands that are happening are in here. But and this is sort of included in the base snake that you get here. And you can return lots of things in there. Like, you can return, like, what's leading into that. You can return logic.\u003C/p>\u003Cp>You can return things like shouts, which are just like little statements that are in your code. We kinda got rid of them. It used to be that you could do custom shouts that were displayed on the board of the games, which was very cool, but, they kinda disappeared. But, okay. Where where do we wanna start, Kevin?\u003C/p>\u003Cp>Speaker 0: Well, the nice thing about this starter snake for learning is it talks us through some steps. But before we do that, why don't we look at the code of what our battlesnake did? So it didn't just go in the same direction nonstop. Oh, that's another note. If your sync doesn't respond within 500 milliseconds or responds in an invalid way, it will continue to take the last direction There are a couple of variables here just to help us out.\u003C/p>\u003Cp>One that tells us where our head position is, and that'll give you an x y position coordinates and our neck, which is the second piece of your body. It's not all of them. It's just your head and your neck. There's a reason why your neck is important. We'll talk about that in a moment.\u003C/p>\u003Cp>And all we're doing here is oh, it's actually a lovely lovely and commented here. You can turn back into yourself. So if you're going straight up, you can go down, but you die immediately because you collide with your body. So all this logic is doing is saying don't hit the neck. Like, don't go back into the last space I was in, which is always the second position of the body.\u003C/p>\u003Cp>Now another thing that's interesting, if I may just kind of embellish kind of what's here, is we're starting to see the data structure play out a bit here. So this move function has an argument called game state. We see on lines 53 and 54 that inside of game state, there is an object called you. And inside of that, there is an array called body. And we can immediately assume that they're full of x, y coordinates.\u003C/p>\u003Cp>And in fact, that's that's made clear on line 56 where my neck and my head both have an x property. Before we continue, what might be interesting oh, I'm sorry. And so so the rest of the logic here is turn up, down, left, or right in that in that array, in that, object at the top, make them force if it means I'm gonna collide with my neck. Remove that as an option. And then I believe what it does is it basically, yeah, randomly picks 1 on line 87 and then returns the randomly selected valid move of which just doesn't collide with your neck.\u003C/p>\u003Cp>It does nothing else that's fancy. That's what we'll be doing today. Before we continue on this, can I make one recommendation that we do go back over to the docs and look at the game object so we understand what else we have at our disposal? Because I think it's important. Sorry.\u003C/p>\u003Cp>Not the get yeah. The game object. The board?\u003C/p>\u003Cp>Speaker 1: Yeah. It's the board object.\u003C/p>\u003Cp>Speaker 0: The board. The board. Perfect. Bottom left is 0. That's we're calling that out because when you draw on screen, sometimes 0 is the top left.\u003C/p>\u003Cp>If you do math, it's in the bottom left. So just note, 0 zero bottom left. That one screws me every time. This is what the object looks like. Could you zoom in a couple of sets by any any chance?\u003C/p>\u003Cp>Cool. Yep. So we have a height and a width. That's the size of the board. That is obviously important to know.\u003C/p>\u003Cp>We have the position of all the food, the position of all the hazards, which we're not playing within the classic game style. But if you hit hazards, I does your health go down or do you die?\u003C/p>\u003Cp>Speaker 1: You die if you hit a hazard. Or well, depends on it depends on the game mode, but a lot of time you'll die.\u003C/p>\u003Cp>Speaker 0: Fine. And then you have an array of snakes. Each snake has the idea of the snake, but it also has look at that. That was that was good teamwork there. It also has its health, all of its body parts, a convenience, which is an object called the head, which is always the first option in the body.\u003C/p>\u003Cp>So that's just a convenience there. They're always the same. The length, any customizations. But really what we care about for logic realistically is the body array and the head as a convenience. But we could get most of what we need from the body and the board collectively.\u003C/p>\u003Cp>What else is there here? Rule set, the game itself, but I don't think we care too much about those in this very moment. Yeah. Yeah. And I\u003C/p>\u003Cp>Speaker 1: think the only other thing that's kind of in here that's, I mean, less it's less valuable. This is more like you you can see. Like, imagine that if you're in this game, your snake can see everything about the other snakes. So, you're also sending all this information about your snake to all other snakes on the board. So\u003C/p>\u003Cp>Speaker 0: Yeah. You could stock out snakes that are low on health.\u003C/p>\u003Cp>Speaker 1: Yeah. Yeah. And some people even decide, like, I don't wanna ever, I don't ever wanna attack a purple snake or a pink snake, and they'll just decide, like, I'm gonna avoid all of those snakes. Just and you have the ability to do like that. These weird kind of logic pieces, which I think is kinda fun.\u003C/p>\u003Cp>Speaker 0: Yeah. Sure. Cool. So that is the shape of the object, which I think was important to dig into as we were starting to get a sense of what it looked like. So we know that we've got a board.\u003C/p>\u003Cp>We know we've got, we know we've got, sorry, a game. We've got boards. Boards have all the snakes and the board state itself. Snakes have a head and a body. So I think that's basically all we need to work through here.\u003C/p>\u003Cp>So all this snake does right now is avoid its neck. That is all it does. So Yep. What is the step one suggestion?\u003C/p>\u003Cp>Speaker 1: So step 1 is to prevent our Battlesnake from doing exactly what it did by moving out of bounds.\u003C/p>\u003Cp>Speaker 0: Okay. So this I mean, logically I mean, you and I have both done this kind of more simple battlesnake a a ton. But, you know, we will run through it kind of, you know, we come with a little bit more confidence in the approach, so we'll talk about it. But just know that in this little bit, there'll be a little less trial and error. Just so don't feel bad if if you're working through this and it's taking you a little longer.\u003C/p>\u003Cp>We just we just happen to know these first few steps like the back of our hand. So right here, there are a couple of comments that will go into that nested object and create a couple of variables for us, board width and board height. In this game, 11:11, but they can be smaller. They can be larger. And all we wanna do is say sorry.\u003C/p>\u003Cp>Go ahead.\u003C/p>\u003Cp>Speaker 1: No. I'm just gonna say, my eyes my eyes are burning right now. Do we see a do we see a dark mode?\u003C/p>\u003Cp>Speaker 0: It will exist. It has to exist. This is shocking. Our confidence. It it has to exist.\u003C/p>\u003Cp>Do you need to save save the snake or does it autosave or something?\u003C/p>\u003Cp>Speaker 1: No. No. So everything auto saves. Okay.\u003C/p>\u003Cp>Speaker 0: I recognize it.\u003C/p>\u003Cp>Speaker 1: I'm just, like, I'm motivated for dark mode, but I also\u003C/p>\u003Cp>Speaker 0: Does it take your does it take your, like, theme of your\u003C/p>\u003Cp>Speaker 1: Oh, look at you. Look at you, Kevin. Look at you figuring out the smart thing. I bet you it does. That's the wrong one that I want to turn.\u003C/p>\u003Cp>Where's my dark mode? Mode? I wanna click\u003C/p>\u003Cp>Speaker 0: on it\u003C/p>\u003Cp>Speaker 1: here, and then I wanna set it on dark theme. And then I bet you if I refresh this, it's gonna be dark.\u003C/p>\u003Cp>Speaker 0: Yeah. Okay.\u003C/p>\u003Cp>Speaker 1: I'm very confident that that's going to happen, but it may\u003C/p>\u003Cp>Speaker 0: not Oh, did I say I'm really confident? That's where it all falls apart. Yeah. We've already learned that that that's how it works.\u003C/p>\u003Cp>Speaker 1: Nope. Did not happen. Here we are. At least it's dark in some places. Is it\u003C/p>\u003Cp>Speaker 0: in your user settings?\u003C/p>\u003Cp>Speaker 1: It might be. It's odd that it's not actually, like\u003C/p>\u003Cp>Speaker 0: Like, here in the editor itself. Yeah. Maybe in your account, that you set up, like, your your default editor settings.\u003C/p>\u003Cp>Speaker 1: Let's see. Let's see. Billing, account, themes?\u003C/p>\u003Cp>Speaker 0: None of this. Themes. Themes. You know what? I wonder if that editor has, like, a command palette.\u003C/p>\u003Cp>Speaker 1: Oh, how\u003C/p>\u003Cp>Speaker 0: do I do that? Boom.\u003C/p>\u003Cp>Speaker 1: Beautiful. Are you gonna catch it here? Do I gotta refresh again? Oh, you're gonna make me refresh again. Alright.\u003C/p>\u003Cp>I see you. I see you.\u003C/p>\u003Cp>Speaker 0: There we go.\u003C/p>\u003Cp>Speaker 1: Beautiful. Oh, look at that. We got some dark mode battle snake as well. But oh, actually, does this still exist? There used to be.\u003C/p>\u003Cp>Yeah. You can. Dark theme. There we go. Nice.\u003C/p>\u003Cp>Speaker 0: All dark Okay.\u003C/p>\u003Cp>Speaker 1: All the time.\u003C/p>\u003Cp>Speaker 0: So let's go back to our code editor then. Sometimes in dark mode, things are just slightly less legible. So maybe let's just do one more step up on the font size, if that's alright. Yep. Lovely.\u003C/p>\u003Cp>And right. So we've got the board width, the board height. So real realistically, this code is gonna run every single turn. We're gonna know the size of the board based on these values, and we're gonna know our own heads x y position. And all we wanna do is the very pragmatic, am I on the very far right of the board?\u003C/p>\u003Cp>Yeah. I can no longer go right, left, up, and down. So that's the logic. We kinda have logic that looks similar to this above, but we can write it from scratch. That's also cool.\u003C/p>\u003Cp>Take it away, mister McLean. No. We're gonna see.\u003C/p>\u003Cp>Speaker 1: So you say we're gonna write it from scratch. I, I fundamentally disagree with you, sir. I feel like if we're provided with this code, I feel like we should use it. Alright. Here we go.\u003C/p>\u003Cp>So here, we were looking if your neck was less than your head, then you didn't wanna move left. So we're gonna remove some of these comments, but it's gonna be the same kind of concept that we're doing here. And so let's look at our let's look at our head. So we wanna get our board width is gonna be important in a minute, but let's get our head. So my head x, and then we want our head x if it's less than or equal to or greater than or equal to.\u003C/p>\u003Cp>Speaker 0: Well, hang on. Fin finish the thought. What what direction are we doing here? Left?\u003C/p>\u003Cp>Speaker 1: Yes. So we're doing so we're doing x. So we're doing the x axis.\u003C/p>\u003Cp>Speaker 0: If it's\u003C/p>\u003Cp>Speaker 1: So we wanted to\u003C/p>\u003Cp>Speaker 0: 0. If it's 0, it's on if it's 0, it's on the far left of the board.\u003C/p>\u003Cp>Speaker 1: Yep. So that's\u003C/p>\u003Cp>Speaker 0: all we need to check. If it's left, remove left trim. There is no minus one. You just die. So, double double equals double equals for for a comparison.\u003C/p>\u003Cp>Nice. So\u003C/p>\u003Cp>Speaker 1: that's our head x. So let's do our head y now. So if our head y equals our board height\u003C/p>\u003Cp>Speaker 0: So we're doing the top now. We're going clockwise. We're doing it. We're doing\u003C/p>\u003Cp>Speaker 1: the top now. But the thing here is because you're gonna get your board height, but then you need to be minus 1. So, like, your board\u003C/p>\u003Cp>Speaker 0: height because it's 0 indexed.\u003C/p>\u003Cp>Speaker 1: Exactly. So you're gonna do your board height minus 1. So this is basically saying if my oh, and not my neck. We want that to be my head.\u003C/p>\u003Cp>Speaker 0: Oh, yeah.\u003C/p>\u003Cp>Speaker 1: So if my head y is equal to the board height minus 1, then we're gonna set up to be false. Yeah. Alright. So let's do our next one. So that means that we're not gonna go off to the left of the board too far.\u003C/p>\u003Cp>It also means we're not gonna go up too far, so let's keep going around. So let's do else if my head my head, and let's go x again, and let's go we'll figure out what the in between is gonna be, but let's do board width minus 1 and is going to be\u003C/p>\u003Cp>Speaker 0: It's it's the same. It's just a double equals. The I I literally cannot get behind you doing this clockwise. It's driving me nuts, but we're here now.\u003C/p>\u003Cp>Speaker 1: We're here now.\u003C/p>\u003Cp>Speaker 0: It's so funny. I would I would have gone left, right, up, down, but I think that's because that's, like, how CSS rules are structured. Left, right, up, down. But yeah. Isn't that yeah.\u003C/p>\u003Cp>That's why I was like, what are you doing? Yeah. So this last one is gonna be, down. Right?\u003C/p>\u003Cp>Speaker 1: Yep. And so this one's gonna be my head y equals equals 0 and get rid of that. And this one's going to be, down is false, and then we actually need to change this one to right is false. And so this logic here, if you haven't been following along, is going to be if your head is equal to 0, you can no longer go left. Type of.\u003C/p>\u003Cp>Did I type\u003C/p>\u003Cp>Speaker 0: Hit backspace.\u003C/p>\u003Cp>Speaker 1: Oh, right. Hit backspace. Is move safe right. Yes. So here, if your head is at x is at 0 sorry.\u003C/p>\u003Cp>Your head x is at 0, then you are not gonna be able to go left. If your head y is at the top of the board, you're not gonna be able to go up. If your head x is at the far right hand side of the board, you're not gonna be able to go right anymore. And if your head y is at 0, you're not gonna be able to go down anymore. Cool?\u003C/p>\u003Cp>Do we wanna should we comment this? We're not gonna comment.\u003C/p>\u003Cp>Speaker 0: Get rid\u003C/p>\u003Cp>Speaker 1: of those comments. Who needs comments? You've got a whole video to go along with this.\u003C/p>\u003Cp>Speaker 0: Exactly.\u003C/p>\u003Cp>Speaker 1: Alright. So let's, it should automatically update. I'm gonna just refresh here, and we'll see if it works. Normally, it does. It's pretty good in real time.\u003C/p>\u003Cp>So let's go and see what happens. Let's just create a new game and see if our solution sorted itself out. So all we have to do here is literally just create a rematch. It's gonna go and read in with our new Battlesnake data. We're gonna see what this looks like in dark mode.\u003C/p>\u003Cp>We may be changing back to light mode. It's almost too dark now. I feel like I need a like a beige mode.\u003C/p>\u003Cp>Speaker 0: Like a like a, what is it? Like a Solaris. Is that what it's called?\u003C/p>\u003Cp>Speaker 1: Yeah. There you go. Like a sepia mode.\u003C/p>\u003Cp>Speaker 0: Yeah. Alright.\u003C/p>\u003Cp>Speaker 1: Oh, this is for so long and my wife laughed\u003C/p>\u003Cp>Speaker 0: at me. We also called it Nokia. So in the background right now, all of these moves are happening in the background. If we hit play and we get up to the latest turn, it kinda just starts to slow down, and we only and we see moves in real time. So what we're kinda letting it do now is catch up.\u003C/p>\u003Cp>So when we hit play, we can just watch it all in one go, which is Yeah. Pretty nice. The other\u003C/p>\u003Cp>Speaker 1: great thing that you can do here is you can actually change the playback speed. So if you wanna watch it slower, you can if you wanna watch it faster. We're just gonna leave it as medium. There's also some other things you can do here. Like, as soon as the first turn is ready, you can auto play in these games.\u003C/p>\u003Cp>That's nice. You can also grab, like, a scrub bar so you can see how long the game is gonna last. It's really only good for you. If you're doing that in a competitive game, it's a little bit less fun. And then you can also show board coordinates.\u003C/p>\u003Cp>I actually think this one's interesting because I think this will show folks something interesting in the game. So let's go back here. So we got the board coordinates up there. As\u003C/p>\u003Cp>Speaker 0: you can see\u003C/p>\u003Cp>Speaker 1: here, 0011. So this is the stuff that we're talking about. Right? So when we said the board width, which is technically 11, even though it's 0 to 10, it's an 11 board. So if we'd left it at that and just said board width equals Yeah.\u003C/p>\u003Cp>Speaker 0: The the the upper the upper right wouldn't have worked. It would have let it go off the yeah. Let it have died first. Let let's hit play so you can see what this thing does. So now it should avoid its neck, and it should avoid walls, which realistically means it will die by hitting itself or running out of food in theory.\u003C/p>\u003Cp>Yep.\u003C/p>\u003Cp>Speaker 1: And that's exactly what exactly what happened. Ran into its body. And so this is what we mean here. So, technically, the neck counts as part of the body as does the head, but we've defined the neck in our code. And so he's never gonna run into his neck, but it doesn't avoid him running into anything past his neck or or like Kevin said, running out of food.\u003C/p>\u003Cp>So, and you can see here, like, he's actually surviving a shorter period of time even though we have more code. But that's honestly I think you're absolutely right.\u003C/p>\u003Cp>Speaker 0: That first\u003C/p>\u003Cp>Speaker 1: time was a little bit\u003C/p>\u003Cp>Speaker 0: of a fix. Yeah. Absolutely. Oh, interesting. That should not have happened.\u003C/p>\u003Cp>Speaker 1: That should not have happened. Oh, I see. So head is technically at 0. It might have been because he's at a he's at there's no other safe moves, but that's not true. So what happened on that last move?\u003C/p>\u003Cp>Did he yeah. He went down. Interesting.\u003C/p>\u003Cp>Speaker 0: Let's let's just quickly check it again. When x is 0, if you head back to the code, when x is 0, remove left. When y is right at the top, hold height minus 1, you can't go up. When x is at the width minus 1, you can't go right. And then when y is at 0, you can't go down.\u003C/p>\u003Cp>Interesting. If or else if that Oh. Oh. No. No.\u003C/p>\u003Cp>No. I I I I I got it. This is in a a weird, like, else if else if else if. We we don't want this. We we we want each one to be its own individual statement.\u003C/p>\u003Cp>All of them need to be Oh, true. I love that.\u003C/p>\u003Cp>Speaker 1: If you\u003C/p>\u003Cp>Speaker 0: move the else's and and you change.\u003C/p>\u003Cp>Speaker 1: I love it. Alright.\u003C/p>\u003Cp>Speaker 0: It I'm not sure that's what the explains what just happened, but that was wrong. Like, it needs to be more like that.\u003C/p>\u003Cp>Speaker 1: Alright. Let's see if that fixes things for us.\u003C/p>\u003Cp>Speaker 0: Oh, timing out. Well, no. 42. Oh, that was an error. Let's scroll up.\u003C/p>\u003Cp>Let's see we'll see what happened. Just go all the way up to the top of the error. Alright. What's happening in here?\u003C/p>\u003Cp>Speaker 1: I need to zoom out a bit this a little bit. Just give me one second.\u003C/p>\u003Cp>Speaker 0: Board width is not defined. We made a typo. I think board width. Board? On line 70.\u003C/p>\u003Cp>There is on line 70. Oh, define the variables. Like, let put a let in in front of each one.\u003C/p>\u003Cp>Speaker 1: So what's interesting there is it means it was running our old code before. So that's that's part of the problem is it was actually running our old code. So now if we run this, k. Our battlesnake should be up. Let's do a game again.\u003C/p>\u003Cp>Speaker 0: And the moment you hit create rematch, we can go back to repl it, and we can see that things are happening. We saw that it made it 34 moves at least. That means all the moves have played out, so we can just hit play. It's looking promising.\u003C/p>\u003Cp>Speaker 1: Yep. Yep. Same thing. And it\u003C/p>\u003Cp>Speaker 0: went into its body.\u003C/p>\u003Cp>Speaker 1: I wanna I wanna examine that because I think that's interesting. Right? We had that l tiff. Why why was that not working? Why why do we think that wasn't working?\u003C/p>\u003Cp>Like, where's the logical error in that?\u003C/p>\u003Cp>Speaker 0: Oh, if if you're if you're at the bottom and the right, it'll only run 1. It'll run the first statement it it gets, and then it will.\u003C/p>\u003Cp>Speaker 1: There we go.\u003C/p>\u003Cp>Speaker 0: So you need to you need to get you need to get each of them. So this is now we think working. We'll we'll keep an eye on this one. Let's assume about that, like, logically, this feels good. If it starts moving out of bounds where there are viable options available, we'll come back and revisit this.\u003C/p>\u003Cp>The next step is to get it to collide is to get it to not collide with your entire body. Right now, it's just avoiding the neck, which is the second piece next to the head. We wanna avoid all of the pieces. And once again, Andrew and I have created start to battle snakes dozens, if not hundreds of times over. So what what are we gonna be doing here?\u003C/p>\u003Cp>We're gonna basically be writing a loop. We're gonna go through every piece of the body, and we're going to see if it's if it's next to me just like it was a boundary, we're gonna remove it. So we're gonna actually end up using reasonable amount of the logic. The logic is gonna look quite similar to the one above, but inside of a loop. Great.\u003C/p>\u003Cp>And we don't we don't actually need to do, we don't even need to we don't need to care about the length so much because we can, we can just use a 4, a for of loop for this, which is a syntax, which I really like. So I'll introduce you to a for of loop. John, sure, you cannot you\u003C/p>\u003Cp>Speaker 1: know what we're gonna do. We're gonna do what every good programmer does, and then I'm gonna go to Google and look up how do I do a loop in JavaScript because I have legitimately You\u003C/p>\u003Cp>Speaker 0: got me. You got me.\u003C/p>\u003Cp>Speaker 1: You got me. I it it\u003C/p>\u003Cp>Speaker 0: auto filled. Hang on. Hang on. Head back to Replit. It was helping you out there.\u003C/p>\u003Cp>It was doing it was and also w three schools. So so if you wrote the word for first first of all, it did actually kind of something yeah. Look. It's kind of giving you a hint here. So this is a very vanilla JavaScript loop.\u003C/p>\u003Cp>What this is, there are 3 parts at the top. First thing it's saying is, wow, okay. That's cool, I suppose. So we're initializing the value I with 1. We're saying keep running this loop for as long as I is less than the the the body length.\u003C/p>\u003Cp>And at the end of every iteration before you start the new one up I by 1 and I is a is a number, which you can feed into an array to get an index. There's a nicer way of doing this though, in my opinion, for this use case. So I'm gonna propose comment this out because I wanna just quickly show you a follow-up loop because I just think it's a I think it's a really nifty syntax. So instead of this, we're gonna go 4, and we're gonna do the the brackets again, but not let it autofill, all of that. Let piece where whatever we want.\u003C/p>\u003Cp>We're gonna call it piece of my body. Exactly. You've got one too many, close brackets there. And then you do squiggly brackets, and we enter into the code block. Piece is an is a variable inside of this loop, which will change its value for each iteration.\u003C/p>\u003Cp>So I just think this is yeah. I mean, it if you if you hit tab now, it seems to have already, that code's wrong. I could ain't gonna do anything. I mean, we'll leave it there as, like, as like, you know, we'll base something on this. This is not right.\u003C/p>\u003Cp>This is saying if if my head is in the spot of this piece, which also includes my head, make everything foresight. This just won't work. It will mean no isn't a viable move and it will keep it will keep sending you know, it will send it, I think, an undefined and then we'll die. So this is wrong. But just before we jump into the logic here, so this is another way to write a a loop in JavaScript.\u003C/p>\u003Cp>And another way you can do it is called a for in loop where you change the word of for in, and then the value of a piece will be the index value. So what we could have done there, an exact copy of what was above is saying for let eye of my body. And then I will automatically increment each time. It will give you the, in sorry, in my body. And then I or whatever variable name you give it, will will be a will be a number that goes up.\u003C/p>\u003Cp>The way I remember the difference between of and in, of object in index.\u003C/p>\u003Cp>Speaker 1: I love that.\u003C/p>\u003Cp>Speaker 0: Yeah. So, we'll we'll use the the for of loop. I think it's really nice because when you start to nest them, you can be really descriptive about what that value means inside of the loop. I think it looks nicer. It's a little more concise.\u003C/p>\u003Cp>But there you go. You learn about JavaScript loops. And there are obviously other loop types as well, but these are the more common 3. So what do we care about when we're looking at our body? Well, for every piece, what we wanna know is, is it one space away from us in any direction?\u003C/p>\u003Cp>That's all we wanna know. And if that is the case, we cannot move into that position. Even that strictly might not be enough because oh, no. It will be. That that will be enough.\u003C/p>\u003Cp>That is groovy. So what we wanna check is is our head equal to x? So is it on the same is it on the same x plane? Oh, no. No.\u003C/p>\u003Cp>Sorry. Inside of this initial if on 95. We're we're gonna alter 95 because 95 doesn't work. So we wanna say if it's on the x plane, but my head dot y is piece dot y minus 1, that means we are one space sorry. That means our body is one space below us.\u003C/p>\u003Cp>Down goes off the table. So this would be whatever the line is where we remove down. Game what's it called?\u003C/p>\u003Cp>Speaker 1: Yep. Yep. Let's go grab that.\u003C/p>\u003Cp>Speaker 0: Yeah. It is move is move safe. So if it's 1 under us down, down goes off. So is move safe dot down equals force. On the same x plane, hang on a moment.\u003C/p>\u003Cp>But the y for on the same x we're gonna fiddle with these till they work. This this bit always gets me. I think I might have just done that wrong, but we'll find out. Oh,\u003C/p>\u003Cp>Speaker 1: it's part of the adventure.\u003C/p>\u003Cp>Speaker 0: So, Hannah, if we're on the same x and it's below us if we're on the same Do you\u003C/p>\u003Cp>Speaker 1: need to draw this out?\u003C/p>\u003Cp>Speaker 0: Yeah. Maybe. Maybe.\u003C/p>\u003Cp>Speaker 1: Let's do it. Let's grab,\u003C/p>\u003Cp>Speaker 0: let's grab some It's bothering me. If we're if we're on the same x plane, but it's 1 below but then we're not on the same x plane out. But it's y. I wondered what tool you you were gonna grab there.\u003C/p>\u003Cp>Speaker 1: We'll see if this works. I might pull up some random drawing that I've created. I bet you I am gonna we're gonna see what magical drawing that I have up on the screen here as this loads.\u003C/p>\u003Cp>Speaker 0: What? Can you back a table in there? Because that's all we need is or a Google Sheet even, to be honest.\u003C/p>\u003Cp>Speaker 1: Oh, that's a good idea. Let's do a Sheet sheet. Okay. Let's do, sheets.new.\u003C/p>\u003Cp>Speaker 0: Sheets.new.\u003C/p>\u003Cp>Speaker 1: Sheets.new? Yes. What's happening right now?\u003C/p>\u003Cp>Speaker 0: What is slides.new, docs.new, forms.new.\u003C/p>\u003Cp>Speaker 1: I love it. That's fantastic. Okay.\u003C/p>\u003Cp>Speaker 0: Right. Zoom in. Zoom in. Zoom in. Zoom in a ton.\u003C/p>\u003Cp>Let's go zoom in. Oh, no. Wrong one. Let's go zoom zoom zoom.\u003C/p>\u003Cp>Speaker 1: There we go.\u003C/p>\u003Cp>Speaker 0: And and let's just do, like, we don't need 11. Right? We could just do, like, a 4 by 4 for the sake of argument. Like or up to 5 by 5 if you go to Ian and you go down to 5. Alright.\u003C/p>\u003Cp>Speaker 1: And I do feel like this visualization is really helpful when you're doing this, like, genuinely. I think this is, like, a process that is useful for anybody to do if you're considering doing this.\u003C/p>\u003Cp>Speaker 0: So draw, like, a 3 parts draw, I don't know. Yeah. Let's just yeah. Yeah. Perfect.\u003C/p>\u003Cp>Right. So what we want is if you go if you go right one oh, what's happening? What what we're doing? Just give me one second. This is just a small one to\u003C/p>\u003Cp>Speaker 1: do this.\u003C/p>\u003Cp>Speaker 0: Hold on. Hold on.\u003C/p>\u003Cp>Speaker 1: There we go.\u003C/p>\u003Cp>Speaker 0: Great. Hang on. Hang on. I think I think we needed to do it. Do a little more than that.\u003C/p>\u003Cp>And I would I would leave them as x's so we can notionally talk about the head. So do do maybe, like, make it like a like a u shape. So also fill in b 5 and b 4.\u003C/p>\u003Cp>Speaker 1: I'm gonna you know what? I gotta stop with this x. The the color x is\u003C/p>\u003Cp>Speaker 0: I I I I yeah.\u003C/p>\u003Cp>Speaker 1: Oh, no. Actually, hold on. I could just literally copy and paste. Come on, Andrew. What are you doing?\u003C/p>\u003Cp>There we go. This\u003C/p>\u003Cp>Speaker 0: is how we want it. Let's assume that b 5 is gonna be the head. Right. So the first thing we wanna do is to say, let let's try and do the left position first. Yep.\u003C/p>\u003Cp>So the x is the same and the piece is on x minus 1. So that's that's the first one we're we're gonna do. So x is the same as my piece dot x, but but the my piece dot. Oh my god. What's going on?\u003C/p>\u003Cp>So this is gonna be Sorry. Sorry. Sorry. Sorry. Sorry.\u003C/p>\u003Cp>The y is the same. Yeah. Because we're all because we're on the same level. We're on the same height. That's what was getting me.\u003C/p>\u003Cp>So if the y if the y is the same, the x is minus 1, you remove left. So let's do that first.\u003C/p>\u003Cp>Speaker 1: Alright. So if the y is the same, x minus y?\u003C/p>\u003Cp>Speaker 0: Yeah. Yeah. Yeah. So so let let's do the first if the first half of the statement, let's change those for y's. So if the y is the same, but the x is piece x minus 1 remove left.\u003C/p>\u003Cp>Speaker 1: Love that.\u003C/p>\u003Cp>Speaker 0: Okay. And and it's exactly the inverse for right. So x and sorry, y and y, perfect. We we replace those, but this time it's gonna be plus 1. That's all it was.\u003C/p>\u003Cp>We're we're just struggling struggling with that. So just to be clear, sorry, those first two still wanna be you need to basically inverse all of them. So y Correct.\u003C/p>\u003Cp>Speaker 1: There we go. And then we're gonna return off\u003C/p>\u003Cp>Speaker 0: that will be right. Yeah. And I I don't think we need I'm really glad we pulled out that tool, but I think we can basically invest them now. So copy and paste both of these. So next thing we're gonna do, not like that, is if the x is the same, therefore, the the column we're in is the same.\u003C/p>\u003Cp>That was it. But the y piece is minus 1, then it's under us. So we're gonna remove down. And, yeah. Exactly.\u003C/p>\u003Cp>Xxy y. There we go. That took us that took us a hot minute, but we got there. So that should now prevent us colliding with oh, hang on. Hang on.\u003C/p>\u003Cp>Why? Oh, no. It's fine. On that line 94 piece isn't, like, white, so I thought it was unused. Oh, no.\u003C/p>\u003Cp>It's\u003C/p>\u003Cp>Speaker 1: because I added an s in there. There was a there was an error there. Just because I'm doing the Windows key save.\u003C/p>\u003Cp>Speaker 0: No. No. No. Peace is declared but never used. I don't think that's right.\u003C/p>\u003Cp>Speaker 1: Oh, there it is. It caught it.\u003C/p>\u003Cp>Speaker 0: There we are. There we go.\u003C/p>\u003Cp>Speaker 1: Cool. Oh, but we got a formatting issue? Hold on. So we can\u003C/p>\u003Cp>Speaker 0: No. It was a it was a formatting issue. No? Hold\u003C/p>\u003Cp>Speaker 1: on. Nope. We're still getting a bug here.\u003C/p>\u003Cp>Speaker 0: Someone watching this right now is screaming. They're like 100%. A 100%. I feel like I'm missing it. Scroll to the top of the arrow.\u003C/p>\u003Cp>The top of the arrow is gonna tell you tell you what it is, but we're not at the top of it yet. Keep going all the way up, all the way up, all the way up. More more more more. Oh, okay. Less.\u003C/p>\u003Cp>Speaker 1: We went too far.\u003C/p>\u003Cp>Speaker 0: We went too far.\u003C/p>\u003Cp>Speaker 1: It happens. Hold on. There we go. I just went too far into the first interpreter.\u003C/p>\u003Cp>Speaker 0: Alright. Board meeting No. No. We sorted that. That was before now.\u003C/p>\u003Cp>We did that.\u003C/p>\u003Cp>Speaker 1: Oh, it's this one. Hold on. Hold the phone. Close u.\u003C/p>\u003Cp>Speaker 0: Wow. Capsule consoles.\u003C/p>\u003Cp>Speaker 1: So many consoles. Formatting failed with message. What?\u003C/p>\u003Cp>Speaker 0: And go have I missed something pretty obvious here?\u003C/p>\u003Cp>Speaker 1: No. I think I think that was the that was the issue for let piece of my body. Hey, bro. Yes. I think it was just that s in there.\u003C/p>\u003Cp>Let's try running it again.\u003C/p>\u003Cp>Speaker 0: You can do it. You can do it. I'm getting a hunch it can't do it without some intervention.\u003C/p>\u003Cp>Speaker 1: What have we done? Is it because we do need to declare this let on my body?\u003C/p>\u003Cp>Speaker 0: Oh, yeah. Maybe that that was the grape last time.\u003C/p>\u003Cp>Speaker 1: Kevin lied.\u003C/p>\u003Cp>Speaker 0: There you go.\u003C/p>\u003Cp>Speaker 1: You lied. You said I didn't have to let it.\u003C/p>\u003Cp>Speaker 0: No. I literally did earlier. That's what happened with board width.\u003C/p>\u003Cp>Speaker 1: I feel like it's it's a useful thing to blame other people for your coding mistakes. I feel like that is what it is the sign of a true a true professional.\u003C/p>\u003Cp>Speaker 0: The professionals we clearly are.\u003C/p>\u003Cp>Speaker 1: Yeah. As I as I went to Google to search how to\u003C/p>\u003Cp>Speaker 0: do a\u003C/p>\u003Cp>Speaker 1: loop in JavaScript.\u003C/p>\u003Cp>Speaker 0: What's what's happening right now? It's all taking a little long. Can you can you refresh the web view at the top?\u003C/p>\u003Cp>Speaker 1: Yeah. Let's do that. I feel like that's k. We can close this out for now. Yeah.\u003C/p>\u003Cp>Speaker 0: Yeah. Yeah.\u003C/p>\u003Cp>Speaker 1: Let's see what's going on. I don't know. Yeah. I don't know what's happening. Maybe repllets happen a day.\u003C/p>\u003Cp>I mean, this is the thing. Right? It's like we are dealing with a cloud ID, so it does chug sometimes. But\u003C/p>\u003Cp>Speaker 0: Absolutely.\u003C/p>\u003Cp>Speaker 1: The REPL is booting. It's doing its thing.\u003C/p>\u003Cp>Speaker 0: It is. I will say as much as I love cloud IDs or cloud development environments as they're trying to brand themselves now, I use Gitpod for ages, I use Codespaces for a while, I have moved back to using a desktop editor for things like this. Like, why am I waiting for my editor to get its act together?\u003C/p>\u003Cp>Speaker 1: You're not wrong.\u003C/p>\u003Cp>Speaker 0: So so yeah.\u003C/p>\u003Cp>Speaker 1: There we go.\u003C/p>\u003Cp>Speaker 0: And the GitHub CLI makes it really, like, quite quite nice to to move between PRs as I'm reviewing stuff. So, alright, great. Let's rerun the game. So now it should not die from its own body. So in theory in theory, for a one snake battlesnake, I think it's gonna have a yeah.\u003C/p>\u003Cp>I think it's gonna have a decent life. Look at this. We broke a 100. There we go. This is amazing.\u003C/p>\u003Cp>Wow. This, okay, 170 moves. Let's see what ended up doing it in no. It could be health or it could be eventually, it had no viable options. You know?\u003C/p>\u003Cp>Let's see.\u003C/p>\u003Cp>Speaker 1: Yep. So it's doing all the things. Not running into its tail now, which was entirely possible with our previous code. It's not getting bigger because it's not getting food. So now it's going in that circle because it's avoiding its body, and it's avoiding its tail, and it's avoiding its neck.\u003C/p>\u003Cp>And so I think he might starve out just because he's getting stuck in that circle.\u003C/p>\u003Cp>Speaker 0: Oh, no. Poor buttersnake. Eat something. Look all that food around you, mate.\u003C/p>\u003Cp>Speaker 1: Dang. So, yeah, that's the interesting thing is, technically, he got into that loop where, like, technically, he was right, but we didn't say if you do this more than once, get out of it, which is a thing you can do. And what that, chicken or sorry. What that snake did is a is a a very famous move in the Battle State community called chicken snake. And chicken snake used to, like, go into a corner and would just chase its tail and wait for everybody else to die.\u003C/p>\u003Cp>And it would do really well.\u003C/p>\u003Cp>Speaker 0: Yeah. And you you basically, you are you either other everyone has to die before you or you have to hope food spawns in in well, it it won't spawn where you are though, will it? It won't spawn in a position where a snake is. So actually\u003C/p>\u003Cp>Speaker 1: It can spawn, like, right where oh, you're actually not wrong. That's absolutely\u003C/p>\u003Cp>Speaker 0: right. Yeah. Interesting. Okay. Cool.\u003C/p>\u003Cp>So step 3 is a pretty, a pretty good evolution of what we just did there, which is up until now, we have a single battle snake on our board. But in reality, battle snakes will not be alone on the board. There will be at least one other snake on the on the board. And our job is not only to avoid ourself, but to avoid every snake and all of their body parts. Thankfully, we are effectively gonna get to get gonna get to, like, work on this work on this.\u003C/p>\u003Cp>And in fact, Andrew, I don't think we should copy and paste it. I think we should adapt this because our snake is in the snake's array as well. The might the me, whatever it's called, is again a convenience like the head to just give you the first item. So there's no point doing a loop over all our parts, then a loop over an array which includes us. We should just adapt this immediately.\u003C/p>\u003Cp>Speaker 1: Okay. Great. Let's do it. Alright. So let's go yeah.\u003C/p>\u003Cp>So we've already defined here. So we've got our opponents defined, and we're gonna do that same let again. Right? Like, we're gonna do our loop with around our opponents, or do we wanna remove ourselves from that first?\u003C/p>\u003Cp>Speaker 0: You know or I mean, this is I mean, it's up to you. Right? We can either, yes, remove ourselves from it, run the loop again because who cares? You know, it's just repeating logic effectively. Or we can adapt what is written above in that kind of 90 to a 110 line range to just be the loop.\u003C/p>\u003Cp>Speaker 1: Oh, I see. Okay. Okay. So where where should we start?\u003C/p>\u003Cp>Speaker 0: Us. So we we know here based on line 116, which we should probably comment out because it doesn't have a let in front of it, but we know that game state dot board dot snakes is an array of all snakes including us. I believe we're always first. So let let's create that variable above this loop, first of all.\u003C/p>\u003Cp>Speaker 1: Alright. Let's do that up here. I see. I see what you're saying. Now when you said adapt, I was like, is that not what I'm doing?\u003C/p>\u003Cp>Alright. So let's define that.\u003C/p>\u003Cp>Speaker 0: Yeah. Yeah. We we don't need to build on top of what we've already done here. In theory in theory, we can also do a way above with, the avoid our neck logic that it came with because we're gonna avoid our neck by virtue of this. Let let's leave it for now.\u003C/p>\u003Cp>Let's do a little bit of cleanup at the end, I think. So now we have it's not even opponents. I don't even think that's the right name. It's snakes. These are the snakes, and it includes us.\u003C/p>\u003Cp>We have a loop that will go through our body and do the check. And, basically, all we wanna do here is write a full loop above it, like surround it in a further full loop, and we'll do the same thing. So we'll go let, snake singular because each iteration is going to be a single snake of snakes. We'll do the open squiggly bracket here on line 44, remove that exactly input precisely, and we'll put that outside the outside of the other one. And all we wanna do here is instead of my body, it will be snake dot body.\u003C/p>\u003Cp>Speaker 1: Oh, Kevin. It's like you've done this many times before. That is just like It is. That is beautiful. So in my hearing and\u003C/p>\u003Cp>Speaker 0: the We we don't know it works yet.\u003C/p>\u003Cp>Speaker 1: No. But I like aesthetically, it's very pleasing.\u003C/p>\u003Cp>Speaker 0: Thank you. You're welcome.\u003C/p>\u003Cp>Speaker 1: Thank you.\u003C/p>\u003Cp>Speaker 0: So now in theory, we're running the same loop for all of for all the snakes. Right? Should we test it?\u003C/p>\u003Cp>Speaker 1: Yeah. Yeah. Let's test. Alright. So let's I'm I was wrong.\u003C/p>\u003Cp>We do need to stop and start it. So let's see. This should work fine. Perfect. Okay.\u003C/p>\u003Cp>Let's head back over here. Gotta do a new game now, because we What? Are actually gonna add\u003C/p>\u003Cp>Speaker 0: another set. Of course. Of course. We may as well use one of these bot snakes.\u003C/p>\u003Cp>Speaker 1: I love that scared bot just disappears in this dark mode.\u003C/p>\u003Cp>Speaker 0: In dark mode.\u003C/p>\u003Cp>Speaker 1: Just not there. That's fine. It doesn't matter. We're gonna add him, and we're gonna add in ready, set, and battle snake.\u003C/p>\u003Cp>Speaker 0: Right.\u003C/p>\u003Cp>Speaker 1: Alright. Let's add that in there. Alright. And so we've got a game running now. Now this is another thing about playing this is, like, you can see a lot of these things from your logs.\u003C/p>\u003Cp>So if you're ever playing BattleSync competitively and, like, there's a competition, like, it's almost like you have to close your logs. Otherwise, it gives too much away.\u003C/p>\u003Cp>Speaker 0: It's like\u003C/p>\u003Cp>Speaker 1: you know Woah.\u003C/p>\u003Cp>Speaker 0: What happened. The other the other nice thing about this is, of course, we get the whole board state every time. So as we start to integrate this with Directus, we can we can consume and store as much of this as we want. Let's, let let's play it. Yeah.\u003C/p>\u003Cp>Game over. 101 moves. Let's see what happens.\u003C/p>\u003Cp>Speaker 1: Okay. So avoiding body, avoiding other snakes as well. Oh, I think I know\u003C/p>\u003Cp>Speaker 0: how this is. Yeah. I think I know how it's gonna end for this snake.\u003C/p>\u003Cp>Speaker 1: Yeah. Me too.\u003C/p>\u003Cp>Speaker 0: Womp womp.\u003C/p>\u003Cp>Speaker 1: Womp womp. Womp womp.\u003C/p>\u003Cp>Speaker 0: And look how close the food is. Boom. Game over. So it can can we do a rematch? Can we get something hopefully a little more vibrant?\u003C/p>\u003Cp>Speaker 1: Let's do a rematch.\u003C/p>\u003Cp>Speaker 0: Let's see\u003C/p>\u003Cp>Speaker 1: what happens here. Yeah. And we'll play it just so that we can see it in real time. And and this way, you can actually see this What\u003C/p>\u003Cp>Speaker 0: is going on with our snake? Why is it happening? Our snake actually oh, there's an error with our snake because there was a viable direction there. It was to go right, and it did not. No safe moves detected.\u003C/p>\u003Cp>So that makes sense. So why was it no safe moves? So we have it set that it's not going to go is\u003C/p>\u003Cp>Speaker 1: that it's not gonna go no. Because he's been going up there. So she should just\u003C/p>\u003Cp>Speaker 0: the final move? He should have gone right.\u003C/p>\u003Cp>Speaker 1: Yeah. He should have gone right or\u003C/p>\u003Cp>Speaker 0: he should\u003C/p>\u003Cp>Speaker 1: have gone wrong. Loop.\u003C/p>\u003Cp>Speaker 0: So our logic is wrong. Let's take a look at that. But but and right was the only move. It couldn't go up based on our heuristics. Even though, in theory, to avoid our body, we can remove the tails.\u003C/p>\u003Cp>We can remove the final piece.\u003C/p>\u003Cp>Speaker 1: But what's interesting is he was doing that. Like, if you look at this, so he was here and he was going here. So, technically, when he was in this spot, he could have actually done this again. Right? Oh, yes.\u003C/p>\u003Cp>So he could have gone up or he could have gone right. Of course. Interesting. Okay. But let's go to our code and see why that didn't so why did\u003C/p>\u003Cp>Speaker 0: you decide? It's because\u003C/p>\u003Cp>Speaker 1: of the other snake. So it is it does have something to do with the fact that he was right next to a snake, and so he didn't think\u003C/p>\u003Cp>Speaker 0: what about oh, no. No. It's right. My head is still correct. And we wanna be checking the piece of every snake.\u003C/p>\u003Cp>Speaker 1: P sex minus 1, p sex plus 1. Yes. That's right. I feel like it's because we do actually need to bring this logic inside because we're running these this and then we're running through this loop.\u003C/p>\u003Cp>Speaker 0: It shouldn't make a difference, though. So? If it's set force once, it's we never set anything's true. We're never undoing the work we did. We're just kind of repeating it.\u003C/p>\u003Cp>You know? Yep. Okay. So I guess my\u003C/p>\u003Cp>Speaker 1: head is in there. We don't need the my body anymore. We're not referencing my body anymore.\u003C/p>\u003Cp>Speaker 0: Sninks does include us. Right?\u003C/p>\u003Cp>Speaker 1: Yeah. Well, let's let's figure that out.\u003C/p>\u003Cp>Speaker 0: I think Let's I think it does. I think you is the name of the the object as a convenience.\u003C/p>\u003Cp>Speaker 1: Snakes snake squad. No. That's not important to us.\u003C/p>\u003Cp>Speaker 0: No. That's the snake squad.\u003C/p>\u003Cp>Speaker 1: That and then board. So, yeah, you are getting snake 1, snake 2, snake 3.\u003C/p>\u003Cp>Speaker 0: Can we see the description? Snakes. All snakes on the game board, including yourself if you have not been eliminated.\u003C/p>\u003Cp>Speaker 1: Yep. So it should be working. You're right.\u003C/p>\u003Cp>Speaker 0: So what is going on here?\u003C/p>\u003Cp>Speaker 1: So for snake of snakes, that's good.\u003C/p>\u003Cp>Speaker 0: Again, people watch it people watching this on director's TV are gonna be like, god. It's so obvious. This is the thing.\u003C/p>\u003Cp>Speaker 1: Well, let's hold on. Let's remove some some let's comment out some\u003C/p>\u003Cp>Speaker 0: I mean, sure. Sure. But, I mean, that's literally gonna\u003C/p>\u003Cp>Speaker 1: matter for anything. But okay. So if my head and let's go back to our let's go back to our sheet, drive.\u003C/p>\u003Cp>Speaker 0: 94% of storage used?\u003C/p>\u003Cp>Speaker 1: Yeah. I should probably, go in here and, you\u003C/p>\u003Cp>Speaker 0: know Oh, not pay not pay more for it. Just act stuff. Yeah. That that's the other solution.\u003C/p>\u003Cp>Speaker 1: Okay. So let's say we have let's let's actually reimagine the board state that we were in\u003C/p>\u003Cp>Speaker 0: I love that. Fine. What one step back? I if it's playing, what's going on here? Oh.\u003C/p>\u003Cp>And we just need to go one step back. We're at the end of the game.\u003C/p>\u003Cp>Speaker 1: Okay. There we go. Alright. So let's bring this to we will actually bring this to a 10 by 10 board. And let's pull this over.\u003C/p>\u003Cp>Speaker 0: No. No. No. No. No.\u003C/p>\u003Cp>We we don't need the whole board. We only need, like, however wide. We only need, like, the cluster around around what we're doing. We we don't need to recreate the whole board.\u003C/p>\u003Cp>Speaker 1: Alright. So let's go\u003C/p>\u003Cp>Speaker 0: Hang on. I'll, on my side, I'll take a little a little cheeky screenshot, and I can I can call that out for you\u003C/p>\u003Cp>Speaker 1: if you want? K.\u003C/p>\u003Cp>Speaker 0: If you if you head if you head back.\u003C/p>\u003Cp>Speaker 1: Go back to the game. There we go.\u003C/p>\u003Cp>Speaker 0: Okay. So here's what we're doing. Let's go to our sheet. So we only need 1 like, we we can do it all within here, basically. So put our snake on, a 1b 1, a 2, and b 2.\u003C/p>\u003Cp>The head is b 2. And then, that that snake's gonna go, a 3. There. Yes. One more to the left.\u003C/p>\u003Cp>We can stop there. The rest is irrelevant. So that's that's what the snake is doing. And so first thing we're doing is we're avoiding ourselves. So we're saying can't go left.\u003C/p>\u003Cp>We can't go. The thing that's got me is why is it going right? That is I'm sorry. Why isn't it going right when that is a legal option? Yep.\u003C/p>\u003Cp>That's the question. The right logic is bust.\u003C/p>\u003Cp>Speaker 1: Okay. So let's look let's pull this side by side. Let's grab this over here and let's go get this over here in our logic and see if we can make sure that our logic addresses this situation. Alright. So let's go here.\u003C/p>\u003Cp>Okay. So yeah, our first code here, my head, y.\u003C/p>\u003Cp>Speaker 0: Yeah.\u003C/p>\u003Cp>Speaker 1: So my head, y, is currently at\u003C/p>\u003Cp>Speaker 0: On on b. Sure. Yeah. Right. On on 1.\u003C/p>\u003Cp>If it's 0 indexed in the bottom left. Yep. So we're both on 1. Sure. And the x, there is a piece one step down.\u003C/p>\u003Cp>Correct. Oh, hang on a moment. I think we've got our things muddled here. If my head y, but x is my so now that's correct. So that that accounts for a 2.\u003C/p>\u003Cp>Left is no longer viable. Yep. Going down 1, it's the same. We're on the same column, but right, this one is what's got me, but the piece dot x plus 1, but that is not the case. It's an it's an and statement.\u003C/p>\u003Cp>So, you know, both those things have to be true for it to be met. So if y is the same, but the my head dot x equals piece dot x plus 1, then it's on the right, but it isn't. I don't know what's going on here. My head okay.\u003C/p>\u003Cp>Speaker 1: Well, hold on. So this technically, these are x aligned, but they're y they're not equal for y.\u003C/p>\u003Cp>Speaker 0: They're they're they're y's match. They're wise match. They're on the same column. That means they're the same do you think I've just got the accent? No.\u003C/p>\u003Cp>Speaker 1: No. No. I think that my I'm trying to work this out in my head. So this is\u003C/p>\u003Cp>Speaker 0: Call that x1 or x. No. No. That's yes. Yes.\u003C/p>\u003Cp>No. You're right. You're right. You're right. You're right.\u003C/p>\u003Cp>Sorry. Yes. Oh, sorry. X1, x2, x3, x4, x5. Yeah.\u003C/p>\u003Cp>Y1y2y3y4.\u003C/p>\u003Cp>Speaker 1: Yeah. So right now these are the same x, but they're different y's.\u003C/p>\u003Cp>Speaker 0: Well, the yeah. So if we hang on. Let's let's start at the top. So let's do the left first. So the x matches.\u003C/p>\u003Cp>We've got them flipped, Andrew. We flipped them.\u003C/p>\u003Cp>Speaker 1: Did we? Yeah. Okay.\u003C/p>\u003Cp>Speaker 0: Look, the y is the if the y is the same no, we haven't. We got it right. So if the y is the same, let's say let's take, b 2 and c 2. Right? Yep.\u003C/p>\u003Cp>The y is the same. Yep. So that they they exist in the same yeah. But the x is minus 1. That means underneath.\u003C/p>\u003Cp>No. That means To the to the left. Something on the left. Fine. Then y is the same once again.\u003C/p>\u003Cp>They're in the same row.\u003C/p>\u003Cp>Speaker 1: Are you\u003C/p>\u003Cp>Speaker 0: sure we haven't flipped them?\u003C/p>\u003Cp>Speaker 1: I I think we might have hold on. So okay. My head y, my piece and piece y are equal. So yeah. You're right.\u003C/p>\u003Cp>These 2 are technically there, and then my head x, so this is my head x, piece x minus 1. So, yeah, this removes left. That that totally makes sense. So we're good there. So next up, my head y equals piece y.\u003C/p>\u003Cp>Okay. Well, what would apply here, I guess? What would apply to this one right here?\u003C/p>\u003Cp>Speaker 0: It would have to be on the right. I think we got it right. I don't know what's going wrong.\u003C/p>\u003Cp>Speaker 1: My head x. Okay. So right now, this is they are the same for these are the same for x. So my head equals piece x. My head y equals piece y minus 1.\u003C/p>\u003Cp>Down is false. Yes. That that's right. That's good. My\u003C/p>\u003Cp>Speaker 0: head pieces We flipped them. We flipped them. And what's getting me is the 1 on 1 on line 107. So my head dot x means they are in the same it means they're in the same row.\u003C/p>\u003Cp>Speaker 1: Yes. It means\u003C/p>\u003Cp>Speaker 0: so it means they're in the same column. It means they're in the same column. Yeah. Right? They're in the same column, but the y piece is minus 1.\u003C/p>\u003Cp>They're in the same column, but the but the y is is 1 to the left. They're in the same column, but the y I I'm struggling to visualize that based on our head. They're on the same Okay. Hold on. So my head ached I think we've, I think we've muddled these.\u003C/p>\u003Cp>I think I think, yeah, these are wrong. And I know we we know this and the audience like, yeah, it's wrong. And here's exactly how, But, I don't think it's as simple as comparing the y's and then comparing the x's. I think only one piece wants to be I think the Right. So can you just put, like, an a in d two for us?\u003C/p>\u003Cp>So we're we're gonna test no. No. No. A d 2. So so so so we're gonna test h versus a.\u003C/p>\u003Cp>Right? So the y is the same. The the y the y is the same. The x is plus 1. The the y is the same and the x is plus 1.\u003C/p>\u003Cp>Yeah. Right gets removed.\u003C/p>\u003Cp>Speaker 1: So right yeah. Right should definitely\u003C/p>\u003Cp>Speaker 0: So but at least one of these is wrong because we're removing too many options. Yep. If if x is the same, so they're in the same column\u003C/p>\u003Cp>Speaker 1: So let's say y here. So these guys are in the same column.\u003C/p>\u003Cp>Speaker 0: But the y is minus 1.\u003C/p>\u003Cp>Speaker 1: Then we're gonna get that here.\u003C/p>\u003Cp>Speaker 0: Fine. Then the y's are the same, the x is plus 1, then we're going right. Dude, this looks correct to me.\u003C/p>\u003Cp>Speaker 1: It does\u003C/p>\u003Cp>Speaker 0: I don't know what's going on.\u003C/p>\u003Cp>Speaker 1: I okay. So I feel like this we've worked through this. I do think it must have something to do with this logic then. Have we screwed something up here? No.\u003C/p>\u003Cp>Speaker 0: I don't think so.\u003C/p>\u003Cp>Speaker 1: If my head equals 0, head x equals 0.\u003C/p>\u003Cp>Speaker 0: It's anywhere in that line. You can't go left. If it's at the board if y is at the height, you can't go up.\u003C/p>\u003Cp>Speaker 1: So here, this was at the height minus 1. So wait. What is height returning? Am I wrong? Have we changed the rules, and is height actually returning the height of the board and not 11?\u003C/p>\u003Cp>Oh, no. No. Returns 11. Oh. Oh.\u003C/p>\u003Cp>Wait. No. That\u003C/p>\u003Cp>Speaker 0: No. That's correct.\u003C/p>\u003Cp>Speaker 1: That's correct.\u003C/p>\u003Cp>Speaker 0: Board width minus 1.\u003C/p>\u003Cp>Speaker 1: Yeah. So that's that's fine. That would be the one that would have mattered that it would have removed the right, and that logic makes sense.\u003C/p>\u003Cp>Speaker 0: Yeah.\u003C/p>\u003Cp>Speaker 1: It's not there. Yeah. This logic is all correct. Let's run another game and see.\u003C/p>\u003Cp>Speaker 0: We are. I am full on at the point where I'm just gonna find a snake because I know I know it's less fun. I know it's less fun, but I just, but I also I also don't don't fancy doing this forever. Let's take a look.\u003C/p>\u003Cp>Speaker 1: K. I wanna see how this game plays out if something similar happens. K. Why did you run into his head? Why was that a safe move?\u003C/p>\u003Cp>Okay. What was the logic there? Did you have no safe moves again? No. So here, he thought he could go right.\u003C/p>\u003Cp>So oh, no. Because there. Okay. That makes sense. That tracks because he wasn't directly next.\u003C/p>\u003Cp>So there's no look ahead right now. So this takes\u003C/p>\u003Cp>Speaker 0: So that's so so that'll happen sometimes.\u003C/p>\u003Cp>Speaker 1: Yeah. So that tracks. That that game worked the way it should have. There's no buggy things there. It worked the way it should.\u003C/p>\u003Cp>Let's see if we can get another game because I feel like maybe that is it. Like, our code is right and there was just some sort of a glitch, but I don't know. K. So he's going in the circle. He's going in the circle.\u003C/p>\u003Cp>So he's gonna starve out anyways. We're not gonna get to see much there. Anyone? That's so weird. Can we can we\u003C/p>\u003Cp>Speaker 0: run it again and again and again?\u003C/p>\u003Cp>Speaker 1: So he's not making the same mistake again? I don't\u003C/p>\u003Cp>Speaker 0: I don't like that it's just doing that. That feel it feels weird to me that it keeps chicken whatever you call it. Chicken Chicken snaking? Chicken snaking. That feels very odd to me that it does that consistently.\u003C/p>\u003Cp>Speaker 1: Okay. So he's not doing this\u003C/p>\u003Cp>Speaker 0: as well. It isn't. Okay. Fine.\u003C/p>\u003Cp>Speaker 1: Okay. And, again, so this ish situation, was that a safe move? So he's gotta finish the game.\u003C/p>\u003Cp>Speaker 0: Come on. Come on. Go back.\u003C/p>\u003Cp>Speaker 1: Okay. So this does that make sense? Yeah. Again, this one makes sense because he didn't see right? So every one of these games, it's just the one game that was the one that was very strange.\u003C/p>\u003Cp>I think that there was some sort of a there was some sort of a an issue there with the actual way that it was reading our logic. I I hate seeing bugs that don't get resolved, but it seems like it's not happening again.\u003C/p>\u003Cp>Speaker 0: It seems to have been okay since.\u003C/p>\u003Cp>Speaker 1: Yeah. Well, let's try again. Maybe Okay. So why did he do that? So he shouldn't have done that either.\u003C/p>\u003Cp>Okay. So here's the thing. So why did he do that? So right. Right.\u003C/p>\u003Cp>Right. Game over. So he should not have gone right there. Why did he go right? Why did he think right\u003C/p>\u003Cp>Speaker 0: Something's wrong. Was a safe move.\u003C/p>\u003Cp>Speaker 1: Okay. So let's look at this again. And we're gonna have these guys here. So let's go, I'm actually gonna do color with these this time because I feel like that will actually help us. And then we'll do here and then we'll do him gray green\u003C/p>\u003Cp>Speaker 0: and gray, green, green, green. That's all. I'm literally this is driving me potty.\u003C/p>\u003Cp>Speaker 1: It's alright. This is the joy. I and, we'll fix it in post. Right?\u003C/p>\u003Cp>Speaker 0: Yeah. We'll fix it. We'll fix it in post. Okay. Okay.\u003C/p>\u003Cp>Do do do do you want them to know what lovely editor, Nat? Let's jump cut here to the point where we fix it, and then we'll explain what went wrong.\u003C/p>\u003Cp>Speaker 1: Yeah.\u003C/p>\u003Cp>Speaker 0: Welcome. Welcome back. We worked it out. Andrew did a lot more like debugging, adding print statements. Andrew, do you want to show the code?\u003C/p>\u003Cp>And for those of you who are, let's zoom in like many, many, many times over, what was the issue? I'm yeah. But but what was the issue?\u003C/p>\u003Cp>Speaker 1: So the issue was we had things flipped with the Yes.\u003C/p>\u003Cp>Speaker 0: We did.\u003C/p>\u003Cp>Speaker 1: Actual thing that we were removing. And the way that we discovered that, and this is kind of an interesting way to do it. I actually, like, find this as a really good way to do any debugging\u003C/p>\u003Cp>Speaker 0: because I\u003C/p>\u003Cp>Speaker 1: threw a console log into each one of these pieces with whatever we had been removing. So, initially, we so let's, like, put this back to what it was before. Originally, this was is moved left, right, down Down\u003C/p>\u003Cp>Speaker 0: down and up.\u003C/p>\u003Cp>Speaker 1: Yep. And so, originally, we had this. And so I wanted to look here and see where, where we sort of messed up here. And so if you look sort of in this log so, and I think this is the latest game. Is it the latest game?\u003C/p>\u003Cp>Speaker 0: No. We ran it once more since. We ran it\u003C/p>\u003Cp>Speaker 1: once more since. Did he did he go out for the same reason? That's my question.\u003C/p>\u003Cp>Speaker 0: Let's find out.\u003C/p>\u003Cp>Speaker 1: Oh, no. Because this was what he was buying correctly. So Yeah. Did I see\u003C/p>\u003Cp>Speaker 0: But but but but but we we we don't necessarily need to find it or or replicate it. But, yeah, you you basically post it in a in the state just before you died, just before it died. Yeah. And then where and then did kind of what we did with the spreadsheet again. And, basically, we had it flipped.\u003C/p>\u003Cp>So up and down needed to flip, left and right needed to flip. And then our snake has also been a lot more interesting as a result.\u003C/p>\u003Cp>Speaker 1: Yeah. So technically, like, even with bad code, it worked.\u003C/p>\u003Cp>Speaker 0: Change it back. Change change it back now before\u003C/p>\u003Cp>Speaker 1: Yeah. Before we forget. So let's go and I'll actually go in here, and I'll remove these console logs because they're false now. Let's go right. Mhmm.\u003C/p>\u003Cp>And remove this one and left. And and I feel like this is kind of the fun of this game is it's like you orienting yourself is also, like, I think it's part of the challenge here. But now, you'll see here so, like, well, in that last game, he didn't follow the rules like we thought he was going to. Now he, she, they, if we go back in here and create a rematch, we're going to see that, like Kevin said, this game is far, far more interesting now. And so here so now he's not constantly going in that loop.\u003C/p>\u003Cp>He's not running into these other snakes. And there we go. Now we're starting to get\u003C/p>\u003Cp>Speaker 0: some wins. Yes. Now I think that might be where we call this snake because the reality is this snake's doing pretty well. For the extent of our series, we don't need to do much. However, there are some other end statements here.\u003C/p>\u003Cp>Did our snake die or did no. No.\u003C/p>\u003Cp>Speaker 1: No. No. No. That was good. Nope.\u003C/p>\u003Cp>We did the right thing. He he just did head to head.\u003C/p>\u003Cp>Speaker 0: So head to head is the other thing to consider. If you collide with a snake head to head in any configuration, right, if you both enter the same square, the snake with the lowest to health, I think. Right? Yeah. Or the shortest snake?\u003C/p>\u003Cp>Speaker 1: Yeah.\u003C/p>\u003Cp>Speaker 0: No. Which one? I just gave you 2 options. Oh, sorry. Shortest so it's it's\u003C/p>\u003Cp>Speaker 1: the snake snake with the it's the snake with the lowest health, I believe. No. Wait. Hold on. Let's talk a little bit.\u003C/p>\u003Cp>The shortest snake. Always consult the docs whenever you're not sure.\u003C/p>\u003Cp>Speaker 0: And so and so what you will need to do then is also look around the heads of each snake and have an exclusion zone of one one valid direction in any way they can go. You can also remove the tail as well because next move, the tail is gonna have moved out of place. So these are the little bits of logic you can do. The longer Longer about the snake\u003C/p>\u003Cp>Speaker 1: will survive. Shorter will be eliminated. If they're both the same length, they both die.\u003C/p>\u003Cp>Speaker 0: Fine. Fine. So there is more logic we can add to this, Nate. But the reality is it's gonna perform pretty well. Apart from head to heads, it should perform or running out of food legitimately because it currently is just hitting food randomly.\u003C/p>\u003Cp>It should perform pretty well. Good enough for ready set battlesnake. But, yes, in reality, you'd also have an exclusion zone around heads, and you would be able to take the last item of each snake body and remove that from your from your checks because it's going to move out of the tail space. Or rather, the tail will not be in that space anymore. So you can stop.\u003C/p>\u003Cp>Ahead might be, but the tail won't be. Sick. Okay. Ready, set, battle snake episode 1. I think the last thing I'd love to do before we just say bye and and stop recording, shall we just do a little bit of cleanup on this on this, file from the top to the bottom, make it feel good, and then we can wrap up.\u003C/p>\u003Cp>And next episode, we will take this snake and see if we can run it inside of a director's project.\u003C/p>\u003Cp>Speaker 1: Amazing. Alright. So, yeah, let's clean this up. Let's leave those comments in there because I think they're great. Let's leave that step 4 in there as well.\u003C/p>\u003Cp>So let's go up here. Now we do need to add a couple of things. So let's do, let's call this Directus TV as our author. And we're going to leave our stuff until next week, I think\u003C/p>\u003Cp>Speaker 0: -I think so. -\u003C/p>\u003Cp>Speaker 1: for our default. But, we've got that in there. This is all good. This is all good. Do we wanna leave this or do we wanna move this inside?\u003C/p>\u003Cp>What do we wanna do, Kevin? What do you think?\u003C/p>\u003Cp>Speaker 0: What do you mean inside?\u003C/p>\u003Cp>Speaker 1: Like, do we wanna deal with moving this logic for the head and neck inside of our inside of our other logic?\u003C/p>\u003Cp>Speaker 0: You can straight up remove it. Leave the variables at the top because my head and my neck might are used. My head is used. My neck is not used. We can remove those.\u003C/p>\u003Cp>They're they're captured in the other logic.\u003C/p>\u003Cp>Speaker 1: Yep. You're absolutely right. Yeah. Okay. So we can remove and we can remove my neck because we don't need that anymore.\u003C/p>\u003Cp>And This is all good. I actually think we should put all of these together. Is there a reason that we should have our board width and height as let and our my head is const, or should we just have them all the same? I feel like they should all\u003C/p>\u003Cp>Speaker 0: the same. It literally makes no difference. Like, in reality, I would make them all cons because we don't reassign them. And I think inside of the 4 loops, you can also use const instead of let, if memory serves me right.\u003C/p>\u003Cp>Speaker 1: I feel like there's probably a way to select all of the lets in here.\u003C/p>\u003Cp>Speaker 0: Command d. Command d. Or control control d. Control\u003C/p>\u003Cp>Speaker 1: d. There we go. Let's call these all const. There we go.\u003C/p>\u003Cp>Speaker 0: Lovely. And the reality is we're still being overly verbose here. Like, line 79 could just be put inside of line 81. Like, we don't we don't need to be declaring all these variables, but I think this is fine. We're gonna move it over next time.\u003C/p>\u003Cp>I think we said we're gonna we're gonna try using direct automate to make this work, which I'm really excited about. There are some interesting quirks. I'm not sure I've quite figured out yet. We have a a pressure based rate limiter that I think we might hit, so we'll we'll have to explore that. But we'll we'll see.\u003C/p>\u003Cp>Speaker 1: Yeah. But I think that's I think that's everything. I think we've pulled out any unnecessary variables in here. I think this is looking pretty pretty good right now.\u003C/p>\u003Cp>Speaker 0: Sounds good. Great. Hit Save, and let's let's wrap up. I think this was a really nice first episode. Very happy we cut that that bit in the middle of working out what we were doing wrong.\u003C/p>\u003Cp>So thank you so much for joining us for the first episode of Ready, Set, Battlesnake. We won't see you next week. This is every 2 weeks. So if you are watching at the time it's coming out and not binge watching at the end, you will find the next episode right here on DIRECTV in 2 weeks' time. I've been Kevin.\u003C/p>\u003Cp>I've been Andrew. There we go. And until next time, you ready for the catchphrase?\u003C/p>\u003Cp>Speaker 1: Ready. Set Battle Snake. We'll get there. We've got 4 episodes.\u003C/p>\u003Cp>Speaker 0: We've got 4 episodes. Bye. Bye.\u003C/p>","Hello, everyone, and welcome to Ready, Set, Battlesnake. I am so thrilled for this season to be happening, bringing together a few of my interests all at the same time, including hanging out with my good friend, Andrew. Hello, Andrew. Hey, Kevin. How are you? I am all the better for seeing you. Would you like to tell us what the hell we're gonna be doing for the next 4 episodes? Yeah. That's a great question. So we are gonna be exploring the wonderful world of Battlesnake. More on that in just a minute. We're gonna be looking at how Battlesnake works. We're gonna be looking at how Battlesnake integrates with some other really cool technologies out there. And, hopefully, by the end of this 4 week series, you'll have a Battlesnake or 4 episode series. You'll have a Battlesnake that is ready to go and to jump in and start, yeah, start competing and playing around and learning some new technologies. Or you can just watch us fumbling around. That's also totally fine too. Also very No. I think what might be good is let's talk about what Battlesnake is and then talk about, like, our affiliations with Battlesnake. Probably feels like a good thing to throw out there. But let's start because we've said the phrase Battlesnake one too many times to not jump into what it is. What is Battlesnake, Andrew? Alright. So if you have ever heard of the old classic game Snake, like that old Nokia snake game where you, sort of control the snake to go around on a screen and eat little berries and get longer, and you try to avoid yourself and avoid the walls and avoid other snakes if they're on the board. Well, Battlesnake is that same idea, but moved around a little bit into the developer space. So it's a really cool way for developers to explore new technologies, new frameworks in this kind of technology agnostic way. So you actually, create a web server that responds to the Battlesnake API, and you can crawl control a snake using code to battle or compete against other snakes in that kind of same gamified way, but, in kind of way more fun for developers. And a few interesting notes here. So firstly, unlike snake on your Nokia, multiple snakes on the board at once, and you're basically trying to outlive everyone else. You die in the same ways, but there are multiple snakes just like this on the screen now. And because what you're doing is implementing an API, it means it's pretty technology agnostic as long as you can host a web server that can have a couple of specific endpoints that accept requests and then responds in a specific way. It almost doesn't matter what technology you use, which is pretty cool. And so we're going to be building this over a few episodes. Today, we're just gonna be focusing on, like, the vanilla Node. Js starter snake and challenges so we can introduce you to this wonderful world. You're gonna love it. And then we'll play around with, like, running Battlesnake in a director's project with directors automate, which should be really fun. And then, towards the end, we're going to also integrate dev cycle, which brings me on to a probably good point here, affiliations. Andrew, do you wanna start with, like, your history with this project? Yeah. Sure. So so I actually started with Battlesink back in 2020. So I joined fairly early on in in the the official Battlesink company. BattleState had been around for about 5 years before I joined the team, and it started out as a community project here on the West Coast of Canada. It was a big sort of hackathon event that happened here where a bunch of people have gotten to a room for a couple of days and built battle stakes and competed in sort of live events. I joined the team in 2020 as sort of their first community manager as we were kind of transitioning, as many were in 2020, into that online world. And so we sort of transitioned the whole kind of BattleState concept into an online platform that folks could engage with around the world. In 2023, Battlesnake, was acquired by another Canadian startup called Dev Cycle, where I currently work as their developer advocate. And so I still get to play around with Battlesnake a lot and work with a lot of the great people that were on the Battlesnake team. And and it's actually like the the pre dev cycle times that, Kevin, you and I met, even though technically, we never met in person until Not in person yet. Mhmm. Absolutely. And then, I, Battlesnake as part of going online, run a bunch of livestream series. It was a really good is still, but was really then in its peak, like, peak pandemic. We're doing everything online, ton of live stream series, including the competitive leagues, shows which discuss different algorithms that can be implemented. And this one show called Coding Badly, which was run with me and my friend, Joe. And this is kind of a spiritual successor to Coding Badly, I think. No offense, Andrew. I think that's probably gonna be the vibe the vibe we go for. So I love Battlesnake. And what's really cool about and we'll discover throughout today is at the very core, you know, there are some very simple rules. Don't hit into walls, don't hit yourself, Don't hit other snakes. Don't run out of life like it. Get food when you need it. But once you start to get options, you can really make some choices here. You know, you can say if left and right are both viable options, is there an algorithmically, beneficial way based on certain strategies to go? Do I box myself off, but now we only have so much life? Do I try and find food as quickly as possible at all cost to also starve out other snakes and so on? So all of this is implemented in code, and then when the games actually happen, your hands off. You know, your your algorithm takes over and makes decisions. So, yeah, I think that's that I I think that's most of Battlesnake. Any other thoughts, Andrew, before we crack on? I mean, let's, I mean, let's jump into the let's jump into the docs because I think they're gonna give us a good idea of kind of the some of the things that maybe we missed. And I think, actually, I'm I'm not a 100% sure. I haven't been in the docs in a while. I may still be featured in, like, the what the heck is Battlesnake doc. So I'm very interested to see if we can get it. Shortly. Am I there? The real question. I'm not oh, wait. No. Hold on. Quick start? There I am. I'm definitely I'm definitely in the quick start. I know this. Am I still here? We'll we'll throw this video into the resources that accompany this video on Directus TV if you wanna go and watch it in full. Awesome. Yeah. So when you click on that, what is dev cycle, it brings you over to the dev cycle documentation, and this documentation is fantastic. One of the things that Kevin was talking about, a lot of those sort of specific things that you can and can't do, if you wanna kinda figure out what that's all about, you can really a great place to go is actually checking out the game rules. And so this is where we start to look at things like this collision idea. So, like, self collisions and body collisions and head to head and who wins in these different situations, and then also giving information about some other things that are really important in this kind of gameplay. So you have a starting position in the game. We'll talk a little bit more about what that looks like kind of as we go through. You have health. That's a dynamic in this game as well. There's food spawns and hazards, and we're not going to get to a lot of that stuff necessarily today. We're gonna see that kind of as we go on. Yeah. But checking out this kind of game rule section is a great way to, like, get a a solid understanding of the idiosyncrasies, I guess, that exist in Battlesnake over sort of the, the the traditional snake game. The other great thing is this rules repository. So, like, all of these rules are actually in, an awesome GitHub repository called Battlesnake Rules. And so if you would rather not watch a video or if you'd rather just do, like, a different kind of deep dive, you can actually, like, jump into the rules repository, which is public, and you can go through and kinda discover a lot of these things yourself. It's all built out in Go. So if you're not a Go developer, then maybe not for you, but it's a useful place to kinda check out all of this written in code. And, yeah, and that was gonna be my clarifying point. This isn't pros. This isn't written out rules. But this is codified rules that actually run within the Battlesnake engine. So you can get right in there and understand again the intricacies of how it all works, which is really cool. I remember possibly in a coding badly episode, definitely in a battlesnake stream in the past generating, battlesnakes that that, like, actively hook into this rule logic very directly, something like that. Okay. So how do we get started? I think we're gonna stick with node. Doesn't matter what language you you use in the real, but, you know, given that directives is all JavaScript view node, and that's at least what I know. That feels like a good place to start. Yeah. I'm with you. 100%. So couple places you can go. Obviously, the quick start that is here. So, basically, anywhere you go in the documentation, you'll find the quick start. This one is set up in Python. So, like, if you watch the video and you've never programmed in Python, it's still kind of valuable, and this will kind of walk you through the game logic. But it also does, in this kind of quick start area, let you choose from different snakes. So I highly recommend if you're just getting started with Battlesnake, click on that quick start, and you'll get the kind of fundamental understanding here. So when we say you are building a web server server that is kind of serving as your battle snake and it's it's interacting with the battle snake API, this is exactly what's actually happening here in this sort of representation here. So, the game engine is querying your web server where your battlesnake is, and your battlesnake server is returning a response. That response can only be 1 of 4 things, up, down, left, or right. That's right. Right. That you received from the game engine, and then you just are kinda repeating this process over and over and over again. So game engine is sending something to your server. Your server is sending something back to the game engine over and over and over again. And, I guess one of the the kind of gotchas here is, the web servers only have 500 milliseconds to respond. And so if you are sort of some people run these on Raspberry Pis and some people or some people run them on you still run them on Heroku servers. And so one of the things that we would tell people is make sure you go and, like, do something on your Heroku server or your Just to see how the code stop at the beginning. Yeah. Exactly. But, but yeah. And and, Kevin, I don't know if I ever told you. Do you know what are the reasons why this 500 millisecond rule was implemented? Have you ever heard this story? No. Sorry. I just realized. I don't think you can see me. So I I'm doing a No. I can. No. No. I can see that. I'm, like, down here. No. I can't. So so I, so in one of the early kind of days of the battle sync competition, before they had this 500 millisecond, like, response time that was there, somebody realized that it was actually faster to respond with a physical keyboard or a game controller than it was to send code back. Love. And so somebody actually went and built a Battlesnake that was hand controlled, and then they would respond in real time. And it's interesting because I think they did fairly well. And at the end of it, it wasn't like, no. You did the wrong thing. It was like, great. You kind of you broke the rule, and now we or or or, like, defined the new rule for us. And so That's funny. I think you could technically still do it, but, you'd have to have some pretty fast thumbs. In planning for this series, not to spoil it, but we have spoken about what it might mean to change strategy, like, mid game. You know, there are there are ways we can achieve this. It was not real time. That's really funny. And the interesting thing about that limit is it really forces you to be computationally efficient as you start to build more complex things. At the beginning, literally, it doesn't matter. You're as long as your server can physically respond in that time, you know, it it doesn't matter. But what you can do with things like at the beginning of a game, spin up some kind of machine learning or artificial intelligence resource, and then per move, you can call on it, but you have to do it very efficiently. So are we gonna start with a starter snake, or are we gonna start from 0? No. No. I think we're I think I think let's start with a starter snake because that's what I think most people are gonna do here. Perfect. And so we'll go to kind of the first step. We're gonna choose our starter project. I'll just open this up in a new tab here. And what I kinda love here is, and I was talking to Kevin before the the show started, is like, so I'm running on a Chromebook right now. So if you're sort of a newer software developer, and you're, like, just getting exposed to this world, and you're, like, I don't have a device that can run this. If you can run a Chrome browser, you can run this. Absolutely. I mean, if you wanna have, like, a crazy setup, like, some people have, like, stand alone servers that run their state, You totally can, but you definitely do not need to do that to kinda get started with this. No. Yeah. Not at all. And so what's also great here too is so you can see these are the official starter stakes that are up on the screen. Yes. They are up on the screen. I'm gonna zoom in a little bit. I'm gonna try and zoom in a little bit. I'm so used to, like, the Mac keys, so I always click the I've used a Windows keyboard on a Mac device, so I'm always hitting the Windows key. Anyways, so You can you can remap them. That's what I had to do. I have a German keyboard on my physical MacBook. I'll just remap the keys in it. Just a few keys on as they're printed, but you just get used to it. Oh, I like that. I should do it. I feel like I've tried it before and it just didn't work. Anyways, not important. So, 5 starter snakes you can start with, which are, like, officially supported starter snakes. So Python, Go, Rust, TypeScript, and then the last one there, JavaScript is what we're gonna look at today. But if you're like, I really wanna do an f sharp snake, there is an f sharp snake for you there. And there's some really cool ones that have been defined in, like, some really, like, interesting languages and setups. Totally worth checking out. But today, we're gonna jump into the JavaScript official snake. Lovely. And what's awesome about these is so you can run these on your own infrastructure. So these are all templates that are set up on there. So you're welcome to run them on there. But we do have these set up ideally if everything works out still. I have not run one of these starter stakes in a while. That makes sense. See if everything works well, is you can just run on Replit. It's just a really simple Node. Js and express server, and, like, there's Docker files for external things, but everything runs right out of the box if everything goes according to plan, which it almost never does. So shall we click the button to show you what? Why would you say that? Now it's all gonna go pear shaped. I know. You say that. No. See, now it goes perfect. You gotta call it out. Right? You gotta call out to that You gotta manifest it. And this is gonna be we're gonna nail it. There's not gonna be any any any errors. And I'll Well Battle Snake's gonna win the league. What's that saying, though? It's like break a leg. Right? You say break a leg before they go on the stage because you're, like, saying the bad thing that might happen. I feel like it's the same thing here. If you just say, like, this is not gonna work at all, it works perfectly. Allegedly. Let's find out. We're gonna see what happens. Alright. So we're gonna click here. We'll see if I'm logged in. I have a feeling that I'm not. You're not. I'm not. But I don't think it really mat I think it'll still spin up if I'm not logged in. We'll find out. We'll see what happens. Nope. I have to log in. That's all good. We've follow look at that. I didn't even know we had followers right now. Alright. I'm gonna close you because we don't need you right now. I'm gonna close you. Yeah. And then when we when we get into REPL, it will bump the font size up significantly, I think. Yeah. So, yeah, it's this easy. So we're literally just clicking on that fork. We're gonna do an auth zero sign in. Hopefully, we can skip past the stage where I'm gonna put my password. Let's go. I think Toby Flender snake is still on Replit 2, which is my which is my battle snake. Oh. This is what this is what I knew was gonna happen. Alright. But you're log but you're wait. You're not logged in. It it hasn't logged you in. Oh, weird. That's weird. Alright. Let's log in again. Let's go log in. Oh, I love it. All the errors, even at the authentication stage, we're running. This does not bode well. Oh, you know what it is? I think it's because I'm like, I need to auth with GitHub. That's probably what it is. I do have it on that email, but I'm not auth with GitHub. So let's go do that. Oh, I hate that sometimes when you log in with, like, an SSO provider, but your email address is the same with that SSO provider and you're like, I don't I don't know which which system. Did it work this time? There we go. Okay. Okay. So let's go. So we're in here, and I probably got a bunch of snakes in here. We'll go back over, and we'll jump into our starter projects again. We'll jump back into our JavaScript snake. And so I've logged in now. Don't need to worry about billing, but this should be a much more straightforward process. I love I said everything is gonna go incredibly terribly. And even at the auth stage, I definitely have jinxed this. I'm not saying I told you so, but I'm certainly thinking it. Alright. So here we go. So this is the this is the snake. I mean, let's get it in the template right now. Yeah. What do we wanna call this snake? We're gonna call this when you call it something does does the dev cycle mascot have a name? His name's ToggleBot. So I don't know. Do we call it, like, we could call it ToggleBot. We could also call it, like, coding badly rebirth. We could call it, we could call it, let's call it let's call it ready, set, battlesnake. Ready, set, battlesnake. Alright. Let's see. I was worried you're gonna be, like, does the does the rabbit have a name? Does the director's rabbit have a name? As far as I'm aware, it doesn't. So set myself in a losing battle there. Let's use the template. I'm very disappointed. To you. Could be right. Could be right. Here we go. So it's forking. If you've never used Replit before, by the way, amazing cloud based IDE, super powerful, integrates with all of the things that you're already using, but also has a lot of inbuilt features. If you've used things like so if you've used Gitpod, which is, or Codespaces, which are sort of in the Code sandbox is another. Code sandbox. Yes. Code sandbox. I find, somebody described it to me recently though that Replit is kind of like the Google Docs or, like, Google Suite of, like, cloud IDEs. It's just, like, very accessible. Mhmm. Yeah. Bump your font size up. Let let's let's make that text more accessible because I can't read the damn thing. Alright. And I already broke stuff. There we go. Let's zoom in. Wonderful. Alright. So let's look at the files that we have here before we start messing around with anything. So it immediately brings us into our index dotjs file. The only other two things that we have in here, ignoring, like, our our package and all the rest of that stuff, are our readme, which is gonna kinda give you, like, things to do, which we're gonna be looking at in that sort of quick start. And then we've got our server dotjs, And this is basically, doing what we described earlier with those different responses that you can do to the Battlesnake API. So there is the just sort of, like, base level. And what what do you call that? Like, the standard with nothing else out of it? The site. There you go. No. No. I mean, like, the the actual just like a slash. Like, no additional Oh, sorry. Sorry. You're referring to that. I would call that the root route. Boom. Root route. So, like, your root route, and we'll talk about kind of what all these things do in a minute. And then you've got your start post route. You've got your move, your end, and then we've got a bunch of other little stuff here, including a console log that actually says your battle tank is running. And if I may just kind of embellish this a little bit. So this main endpoint that we care about is this move endpoint. That is the endpoint that will receive board states and ultimately has to respond with a direction. You'll notice there are these other ones. So this route endpoint handler, route handler there, that will respond with information about your snake. We'll do that in due course. But the start and end are interesting. We mentioned earlier about provisioning resource. That's basically where you would do that. Right? Because I think you get a small grace period between the start endpoint being hit and the first time the move endpoint is hit in any given game. And then your snake can be entered into many games. Yep. Yep. Absolutely. And then yeah. There there's a bunch of stuff. We'll we'll kinda look at some of these and see what they do, but but but Kevin's absolutely right. Like, move is really what matters, and that's where you need to return sort of up down left or right. So the other thing just to look in here, especially for the snake, is just kinda looking at these, like, what you have in this basic package dot JSON file. There's really nothing there. This is a super I think. Super, super light template. And you can go crazy with this, but this is a very, very light template. Mhmm. Okay. So, do we wanna do we wanna run this or should we look through first or should we head back over to the quick start? What do you think, Kevin? I think I think we could do a little less with the quick start because you and I have both like, we should I think we should refer to it as and when it's needed. I say let's get the let's go to the Battlesnake platform. Let's take this basic snake, which I think what what if you scroll down this file, what does the move endpoint do at the moment? Oh, there is some there is some logic there. Yeah. We'll talk we'll talk a little bit about that, but it does do stuff. So let's let's set up this snake and get one game running so we can kinda get to grips with how that code becomes a battle snake in a game. I think that that feels about Kevin, I have one question for you, though. What do you think I mean, Replen is trying to tell us to do this desperately, and we haven't done it yet. What is the thing that we need to do? Oh, I wonder if there's one call to action on this page that is significantly brighter than the oh, okay. Alright. Yeah. There was no mystery. We're hitting run. Awesome. And so this is the piece here. So this is the piece that tells you that your Battlesnake is running, is you're gonna see this API response here. So you're gonna see this API version, the author, color, head, tail. And then in your console, if it's running, you're gonna see this running battle snake at whatever the server, domain is and, or server address is and then info. Now because of the nature of Battlesnake, you need to have a public, URL that you can share with the platform. This is new. They used to all be public and has since shifted. So I think we need to deploy our app. No. We don't need to deploy our app. So we're gonna see what happens. We're gonna see if we can use just this. Yeah. Let's go to new tab. And then I yeah. So it'll be like this, and let me bump this up as well. We'll see how much it bumps up actually. There we go. So this is the same thing that we're seeing here. And then in our, in our navigation bar, we have this, like, replit.dev link, and so that's what we're gonna put over into the Battlesleep platform. It used to show up right here. I don't know why it doesn't anymore. I wonder if it should I imagine I imagine it got a bit unwieldy as you build more complex URLs by that. That's right. That must be it. And that's probably more useful as a developer just to see the the path, but who knows? You know what, though? I have a feeling with a lot of these platforms, it might also be. Yeah. You know what? So the what you can do is if you just, like, right click on this, like, 0.0.0.08 1,000, similar to other platforms, it'll actually convert it to whatever that public URL is. Oh, that's interesting. So it can actually just copy that over there. Okay. So that's working. So let's head back over to play.battlesnake.com. I'm so excited. Play. Play. Tailwind is a very nice little very nice little playground. It is a very nice playground. This is like this Chromebook has become my central development, tool. And so, like, I have so many, like, interesting things on here. I never thought a Chromebook would be my daily driver. Alright. So over on the battle 6 site, auth with GitHub, highly recommend. Not that you need to, but, we're gonna auth there. Oh, look at that. Inclusive waffle. Good. Okay. So I'm logged in with my personal account. So here's what I love. I used to so I'm a teacher. If you didn't know that, what you Oh, really? You have absolutely no mannerisms that would give that away. Like, what should we press on the screen now? Is it the green button? But I I I introduced, my, like, middle schoolers to, to Battlesnake a couple of years ago. And, like, all their snakes are still in here, which I kind of That's fun. Some of these are That's Toby still mine, though, runs on node, on Node RED. On Node RED. That is a stellar platform, if you are looking at, having a stake. And actually, funny enough now now that I think about doing a directus snake I was just about to say it. Yeah. Yeah. So so we have, directors automate as part of that. We have flows, which is a low code automation builder. And, yeah, if I whenever I actually describe it to developers, I ask if they've seen Node RED because I think it's very comparable. You know, you have automation builders that are very much like Zapier, and then you have ones which are more like Node RED. And this is more like that where you're wiring together these these these operations. But, yeah, we'll get to that. I love it. Alright. So, we gotta add a snake in here. Now if you're new in your account, I think it prompts you where to add your new snake. I haven't done this in a little bit, so we're gonna see. I have a feeling I actually need to go into my battlesnakes. Yeah. We're gonna find out. There we go. And here we go. So we got all of our snakes that are currently in here. We're gonna create a new one. We're gonna call this one ready, set, Battlesnake. We're gonna drop our server URL in there that we copied over from do we still have it? Yeah. There it is. I think that's And specifically without anything after the slash. We have to increment those, but that's correct. Yeah. So this is what you're gonna see, something that looks a little bit like this. If you wanna try and copy this and use my snake, go nuts, or use our snake. And then the next thing you choose here is your engine region. And so I highly recommend that when you're getting started, you don't worry too much about which engine region that you're using. There's a good, like, kind of breakdown on why you would use a different engine region. I'm based in in in North America, so and I'm kinda closer to Oregon, so I'm gonna set the state there. Kevin might wanna set it up based in the Netherlands, but it really, really doesn't matter at kind of the base level of Battlesnake. It it matters when you're squeezing out those milliseconds worth of worth of performance, but we're simply not. Exactly. And then, again, description you can add, same with programming language. This is just fun. It gives it a little bit of flair. So you can add a JavaScript, and then we can say this is Platform. We need to we need to do some work here. By the next time we record, we're gonna be doing some work to this list because we'll be we'll be moving we'll be moving our snake over. I love it. Alright. So let's go. And what are we gonna find? What are we hosting on right now? We're hosting on Replit, so we should have Replit there. And what I love about this list too is you can actually see, like, here are all the different places that you could host a snake. Now some of these I highly would not recommend. Like, get out Codespaces. I don't think you can actually keep a long running server going. But Interesting. No. Yeah. Yeah. You have to have the Codespace, and then they go to sleep. Yeah. But lots of different cool options here. And then the other thing you can do is you can set it so that other people can interact with your snake. And so the upside here is that you can allow other people to kind of play around with it. The downside is you can have people that decide to challenge while you're using your snake for some other thing, and then you're having to deal with all the compute costs. And if you're running on, like, AWS or something that charges you for all of those compute hours, definitely challenging. It's not just that. It's every request you get in relation to a single game has an ID of that game. But at basic levels, you're unlikely to be segmenting game logic. And if you're doing, you know, basic battle snakes, we'll just look at a single board and make a single response based on the heuristics of your code, the algorithm you create. But if you're doing something a little more clever way, you're looking over multiple turns, it screws you. But by that point, I suppose you are you are using that ID, but that's worth considering as well. Yeah. Absolutely. Okay. So let's jump in here. So we'll leave it as private. We'll save this battle snake. So you'll know once you save right away if your battle is actually there because it'll show up with the name, obviously, that you set it here. If you don't, though, you won't get any response here in this ping. So you might put, it used to be that if you started a snake on a service, it wouldn't automatically go. And so you might have to run this ping just to kinda see. And you can see here, if we do this ping, we were talking about that 500 millisecond response time. You can see that Replit is really up their, like, their timing. It used to be They didn't. They didn't. That first response was a few hundred. So that must be like a cold start kind of like now it's middling around 40, 50, 60, which is great, but it was multiple 100 the first time you, the first time it was there before you hit ping again out of interest. Yeah. That's true. So But then that's what you're starting to see. A good idea. Yeah. Yeah. Okay. So we've got we've got ready set battlesync in there. You'll also see you kinda get the flare that's related to the the language and and sort of hosting service that you're using. And so now, our Battlesnake is in there and is ready to go. So, do we wanna make our Battlesnake look good before we bring it into the game or do we wanna bring it into the game? Let let's jump into it and maybe we'll see if the the Battlesnake elders allow this. We might see if we can do some custom customization during the the act of us recording 4 episodes. But for now, let's just leave our our snake a slightly boring gray. We can come back to it. When when we come and update the platform to direct us, that's where we'll do the customization. That'll be next time. I love it. Alright. So, we're gonna go in here. You can choose. So, well, in the traditional Battlesnake game, or traditional snake game, I guess, it's kind of a standard map. No hazards. It's kind of just food pops up and things like that. So we're going to choose just the standard mode and a standard board size of 11 by 11. And we're just going to add ready, set, Battlesnake onto the board. We're not going to add any other snakes in there. You can add some bots. These bots are always running that have, like, some set sort of modes, but we're just looking to see if our snake is ready to go. So let's go ahead and see, how ready set Battlesnake runs. Alright. We're in. Okay. We're here. Should we have it now? You have to. Oh, yeah. Yeah. Come on. Let's do that. You ready? I'm so thrilled. No. I'm good. Alright. You ready? Yeah. Ready. Oh, no. No. We have a we we we get a catchphrase for this show. What? If Reggie said that Oh, my goodness. Yes. Okay. Here we go. Okay. Of course. Alright. Let's go. Ready. Ready. Set. Battle snake. That was thoroughly underwhelming. I'm gonna be honest. But but we are watching the snake. We got So this is what we got. So you see the ping? You saw the ping going on the right. So it was still around that 40 millisecond mark. There were 47 turns that happened before our snake died. Why did it die? And the nice thing about this editor, this player, of course, is you can step through your turns. So it was performing kind of a slightly eclectic set of moves, which is great. But what eventually happened to it? So it was at the bottom and it went off the board. It went down. So that was that's that's what our snake did. Now just to be clear, we'll look into the logic of our battle snake next, but we don't quite know why let's look at our code. And now we're at the point where we can start customizing this, this battle snake. Alright. So we're gonna jump back over to Remplit. And so all of the things that you need to know for this snake when you're getting started are all gonna live here. And the other thing that's really cool here that I guess we didn't talk about earlier and maybe you didn't see here because we didn't go that far down in the code is we're actually returning into the console every move. So you can see why it moved or not why it moved in that direction, but you can see what happened. So this in this game start move 0, move 1, all of these commands that are happening are in here. But and this is sort of included in the base snake that you get here. And you can return lots of things in there. Like, you can return, like, what's leading into that. You can return logic. You can return things like shouts, which are just like little statements that are in your code. We kinda got rid of them. It used to be that you could do custom shouts that were displayed on the board of the games, which was very cool, but, they kinda disappeared. But, okay. Where where do we wanna start, Kevin? Well, the nice thing about this starter snake for learning is it talks us through some steps. But before we do that, why don't we look at the code of what our battlesnake did? So it didn't just go in the same direction nonstop. Oh, that's another note. If your sync doesn't respond within 500 milliseconds or responds in an invalid way, it will continue to take the last direction There are a couple of variables here just to help us out. One that tells us where our head position is, and that'll give you an x y position coordinates and our neck, which is the second piece of your body. It's not all of them. It's just your head and your neck. There's a reason why your neck is important. We'll talk about that in a moment. And all we're doing here is oh, it's actually a lovely lovely and commented here. You can turn back into yourself. So if you're going straight up, you can go down, but you die immediately because you collide with your body. So all this logic is doing is saying don't hit the neck. Like, don't go back into the last space I was in, which is always the second position of the body. Now another thing that's interesting, if I may just kind of embellish kind of what's here, is we're starting to see the data structure play out a bit here. So this move function has an argument called game state. We see on lines 53 and 54 that inside of game state, there is an object called you. And inside of that, there is an array called body. And we can immediately assume that they're full of x, y coordinates. And in fact, that's that's made clear on line 56 where my neck and my head both have an x property. Before we continue, what might be interesting oh, I'm sorry. And so so the rest of the logic here is turn up, down, left, or right in that in that array, in that, object at the top, make them force if it means I'm gonna collide with my neck. Remove that as an option. And then I believe what it does is it basically, yeah, randomly picks 1 on line 87 and then returns the randomly selected valid move of which just doesn't collide with your neck. It does nothing else that's fancy. That's what we'll be doing today. Before we continue on this, can I make one recommendation that we do go back over to the docs and look at the game object so we understand what else we have at our disposal? Because I think it's important. Sorry. Not the get yeah. The game object. The board? Yeah. It's the board object. The board. The board. Perfect. Bottom left is 0. That's we're calling that out because when you draw on screen, sometimes 0 is the top left. If you do math, it's in the bottom left. So just note, 0 zero bottom left. That one screws me every time. This is what the object looks like. Could you zoom in a couple of sets by any any chance? Cool. Yep. So we have a height and a width. That's the size of the board. That is obviously important to know. We have the position of all the food, the position of all the hazards, which we're not playing within the classic game style. But if you hit hazards, I does your health go down or do you die? You die if you hit a hazard. Or well, depends on it depends on the game mode, but a lot of time you'll die. Fine. And then you have an array of snakes. Each snake has the idea of the snake, but it also has look at that. That was that was good teamwork there. It also has its health, all of its body parts, a convenience, which is an object called the head, which is always the first option in the body. So that's just a convenience there. They're always the same. The length, any customizations. But really what we care about for logic realistically is the body array and the head as a convenience. But we could get most of what we need from the body and the board collectively. What else is there here? Rule set, the game itself, but I don't think we care too much about those in this very moment. Yeah. Yeah. And I think the only other thing that's kind of in here that's, I mean, less it's less valuable. This is more like you you can see. Like, imagine that if you're in this game, your snake can see everything about the other snakes. So, you're also sending all this information about your snake to all other snakes on the board. So Yeah. You could stock out snakes that are low on health. Yeah. Yeah. And some people even decide, like, I don't wanna ever, I don't ever wanna attack a purple snake or a pink snake, and they'll just decide, like, I'm gonna avoid all of those snakes. Just and you have the ability to do like that. These weird kind of logic pieces, which I think is kinda fun. Yeah. Sure. Cool. So that is the shape of the object, which I think was important to dig into as we were starting to get a sense of what it looked like. So we know that we've got a board. We know we've got, we know we've got, sorry, a game. We've got boards. Boards have all the snakes and the board state itself. Snakes have a head and a body. So I think that's basically all we need to work through here. So all this snake does right now is avoid its neck. That is all it does. So Yep. What is the step one suggestion? So step 1 is to prevent our Battlesnake from doing exactly what it did by moving out of bounds. Okay. So this I mean, logically I mean, you and I have both done this kind of more simple battlesnake a a ton. But, you know, we will run through it kind of, you know, we come with a little bit more confidence in the approach, so we'll talk about it. But just know that in this little bit, there'll be a little less trial and error. Just so don't feel bad if if you're working through this and it's taking you a little longer. We just we just happen to know these first few steps like the back of our hand. So right here, there are a couple of comments that will go into that nested object and create a couple of variables for us, board width and board height. In this game, 11:11, but they can be smaller. They can be larger. And all we wanna do is say sorry. Go ahead. No. I'm just gonna say, my eyes my eyes are burning right now. Do we see a do we see a dark mode? It will exist. It has to exist. This is shocking. Our confidence. It it has to exist. Do you need to save save the snake or does it autosave or something? No. No. So everything auto saves. Okay. I recognize it. I'm just, like, I'm motivated for dark mode, but I also Does it take your does it take your, like, theme of your Oh, look at you. Look at you, Kevin. Look at you figuring out the smart thing. I bet you it does. That's the wrong one that I want to turn. Where's my dark mode? Mode? I wanna click on it here, and then I wanna set it on dark theme. And then I bet you if I refresh this, it's gonna be dark. Yeah. Okay. I'm very confident that that's going to happen, but it may not Oh, did I say I'm really confident? That's where it all falls apart. Yeah. We've already learned that that that's how it works. Nope. Did not happen. Here we are. At least it's dark in some places. Is it in your user settings? It might be. It's odd that it's not actually, like Like, here in the editor itself. Yeah. Maybe in your account, that you set up, like, your your default editor settings. Let's see. Let's see. Billing, account, themes? None of this. Themes. Themes. You know what? I wonder if that editor has, like, a command palette. Oh, how do I do that? Boom. Beautiful. Are you gonna catch it here? Do I gotta refresh again? Oh, you're gonna make me refresh again. Alright. I see you. I see you. There we go. Beautiful. Oh, look at that. We got some dark mode battle snake as well. But oh, actually, does this still exist? There used to be. Yeah. You can. Dark theme. There we go. Nice. All dark Okay. All the time. So let's go back to our code editor then. Sometimes in dark mode, things are just slightly less legible. So maybe let's just do one more step up on the font size, if that's alright. Yep. Lovely. And right. So we've got the board width, the board height. So real realistically, this code is gonna run every single turn. We're gonna know the size of the board based on these values, and we're gonna know our own heads x y position. And all we wanna do is the very pragmatic, am I on the very far right of the board? Yeah. I can no longer go right, left, up, and down. So that's the logic. We kinda have logic that looks similar to this above, but we can write it from scratch. That's also cool. Take it away, mister McLean. No. We're gonna see. So you say we're gonna write it from scratch. I, I fundamentally disagree with you, sir. I feel like if we're provided with this code, I feel like we should use it. Alright. Here we go. So here, we were looking if your neck was less than your head, then you didn't wanna move left. So we're gonna remove some of these comments, but it's gonna be the same kind of concept that we're doing here. And so let's look at our let's look at our head. So we wanna get our board width is gonna be important in a minute, but let's get our head. So my head x, and then we want our head x if it's less than or equal to or greater than or equal to. Well, hang on. Fin finish the thought. What what direction are we doing here? Left? Yes. So we're doing so we're doing x. So we're doing the x axis. If it's So we wanted to 0. If it's 0, it's on if it's 0, it's on the far left of the board. Yep. So that's all we need to check. If it's left, remove left trim. There is no minus one. You just die. So, double double equals double equals for for a comparison. Nice. So that's our head x. So let's do our head y now. So if our head y equals our board height So we're doing the top now. We're going clockwise. We're doing it. We're doing the top now. But the thing here is because you're gonna get your board height, but then you need to be minus 1. So, like, your board height because it's 0 indexed. Exactly. So you're gonna do your board height minus 1. So this is basically saying if my oh, and not my neck. We want that to be my head. Oh, yeah. So if my head y is equal to the board height minus 1, then we're gonna set up to be false. Yeah. Alright. So let's do our next one. So that means that we're not gonna go off to the left of the board too far. It also means we're not gonna go up too far, so let's keep going around. So let's do else if my head my head, and let's go x again, and let's go we'll figure out what the in between is gonna be, but let's do board width minus 1 and is going to be It's it's the same. It's just a double equals. The I I literally cannot get behind you doing this clockwise. It's driving me nuts, but we're here now. We're here now. It's so funny. I would I would have gone left, right, up, down, but I think that's because that's, like, how CSS rules are structured. Left, right, up, down. But yeah. Isn't that yeah. That's why I was like, what are you doing? Yeah. So this last one is gonna be, down. Right? Yep. And so this one's gonna be my head y equals equals 0 and get rid of that. And this one's going to be, down is false, and then we actually need to change this one to right is false. And so this logic here, if you haven't been following along, is going to be if your head is equal to 0, you can no longer go left. Type of. Did I type Hit backspace. Oh, right. Hit backspace. Is move safe right. Yes. So here, if your head is at x is at 0 sorry. Your head x is at 0, then you are not gonna be able to go left. If your head y is at the top of the board, you're not gonna be able to go up. If your head x is at the far right hand side of the board, you're not gonna be able to go right anymore. And if your head y is at 0, you're not gonna be able to go down anymore. Cool? Do we wanna should we comment this? We're not gonna comment. Get rid of those comments. Who needs comments? You've got a whole video to go along with this. Exactly. Alright. So let's, it should automatically update. I'm gonna just refresh here, and we'll see if it works. Normally, it does. It's pretty good in real time. So let's go and see what happens. Let's just create a new game and see if our solution sorted itself out. So all we have to do here is literally just create a rematch. It's gonna go and read in with our new Battlesnake data. We're gonna see what this looks like in dark mode. We may be changing back to light mode. It's almost too dark now. I feel like I need a like a beige mode. Like a like a, what is it? Like a Solaris. Is that what it's called? Yeah. There you go. Like a sepia mode. Yeah. Alright. Oh, this is for so long and my wife laughed at me. We also called it Nokia. So in the background right now, all of these moves are happening in the background. If we hit play and we get up to the latest turn, it kinda just starts to slow down, and we only and we see moves in real time. So what we're kinda letting it do now is catch up. So when we hit play, we can just watch it all in one go, which is Yeah. Pretty nice. The other great thing that you can do here is you can actually change the playback speed. So if you wanna watch it slower, you can if you wanna watch it faster. We're just gonna leave it as medium. There's also some other things you can do here. Like, as soon as the first turn is ready, you can auto play in these games. That's nice. You can also grab, like, a scrub bar so you can see how long the game is gonna last. It's really only good for you. If you're doing that in a competitive game, it's a little bit less fun. And then you can also show board coordinates. I actually think this one's interesting because I think this will show folks something interesting in the game. So let's go back here. So we got the board coordinates up there. As you can see here, 0011. So this is the stuff that we're talking about. Right? So when we said the board width, which is technically 11, even though it's 0 to 10, it's an 11 board. So if we'd left it at that and just said board width equals Yeah. The the the upper the upper right wouldn't have worked. It would have let it go off the yeah. Let it have died first. Let let's hit play so you can see what this thing does. So now it should avoid its neck, and it should avoid walls, which realistically means it will die by hitting itself or running out of food in theory. Yep. And that's exactly what exactly what happened. Ran into its body. And so this is what we mean here. So, technically, the neck counts as part of the body as does the head, but we've defined the neck in our code. And so he's never gonna run into his neck, but it doesn't avoid him running into anything past his neck or or like Kevin said, running out of food. So, and you can see here, like, he's actually surviving a shorter period of time even though we have more code. But that's honestly I think you're absolutely right. That first time was a little bit of a fix. Yeah. Absolutely. Oh, interesting. That should not have happened. That should not have happened. Oh, I see. So head is technically at 0. It might have been because he's at a he's at there's no other safe moves, but that's not true. So what happened on that last move? Did he yeah. He went down. Interesting. Let's let's just quickly check it again. When x is 0, if you head back to the code, when x is 0, remove left. When y is right at the top, hold height minus 1, you can't go up. When x is at the width minus 1, you can't go right. And then when y is at 0, you can't go down. Interesting. If or else if that Oh. Oh. No. No. No. I I I I I got it. This is in a a weird, like, else if else if else if. We we don't want this. We we we want each one to be its own individual statement. All of them need to be Oh, true. I love that. If you move the else's and and you change. I love it. Alright. It I'm not sure that's what the explains what just happened, but that was wrong. Like, it needs to be more like that. Alright. Let's see if that fixes things for us. Oh, timing out. Well, no. 42. Oh, that was an error. Let's scroll up. Let's see we'll see what happened. Just go all the way up to the top of the error. Alright. What's happening in here? I need to zoom out a bit this a little bit. Just give me one second. Board width is not defined. We made a typo. I think board width. Board? On line 70. There is on line 70. Oh, define the variables. Like, let put a let in in front of each one. So what's interesting there is it means it was running our old code before. So that's that's part of the problem is it was actually running our old code. So now if we run this, k. Our battlesnake should be up. Let's do a game again. And the moment you hit create rematch, we can go back to repl it, and we can see that things are happening. We saw that it made it 34 moves at least. That means all the moves have played out, so we can just hit play. It's looking promising. Yep. Yep. Same thing. And it went into its body. I wanna I wanna examine that because I think that's interesting. Right? We had that l tiff. Why why was that not working? Why why do we think that wasn't working? Like, where's the logical error in that? Oh, if if you're if you're at the bottom and the right, it'll only run 1. It'll run the first statement it it gets, and then it will. There we go. So you need to you need to get you need to get each of them. So this is now we think working. We'll we'll keep an eye on this one. Let's assume about that, like, logically, this feels good. If it starts moving out of bounds where there are viable options available, we'll come back and revisit this. The next step is to get it to collide is to get it to not collide with your entire body. Right now, it's just avoiding the neck, which is the second piece next to the head. We wanna avoid all of the pieces. And once again, Andrew and I have created start to battle snakes dozens, if not hundreds of times over. So what what are we gonna be doing here? We're gonna basically be writing a loop. We're gonna go through every piece of the body, and we're going to see if it's if it's next to me just like it was a boundary, we're gonna remove it. So we're gonna actually end up using reasonable amount of the logic. The logic is gonna look quite similar to the one above, but inside of a loop. Great. And we don't we don't actually need to do, we don't even need to we don't need to care about the length so much because we can, we can just use a 4, a for of loop for this, which is a syntax, which I really like. So I'll introduce you to a for of loop. John, sure, you cannot you know what we're gonna do. We're gonna do what every good programmer does, and then I'm gonna go to Google and look up how do I do a loop in JavaScript because I have legitimately You got me. You got me. You got me. I it it auto filled. Hang on. Hang on. Head back to Replit. It was helping you out there. It was doing it was and also w three schools. So so if you wrote the word for first first of all, it did actually kind of something yeah. Look. It's kind of giving you a hint here. So this is a very vanilla JavaScript loop. What this is, there are 3 parts at the top. First thing it's saying is, wow, okay. That's cool, I suppose. So we're initializing the value I with 1. We're saying keep running this loop for as long as I is less than the the the body length. And at the end of every iteration before you start the new one up I by 1 and I is a is a number, which you can feed into an array to get an index. There's a nicer way of doing this though, in my opinion, for this use case. So I'm gonna propose comment this out because I wanna just quickly show you a follow-up loop because I just think it's a I think it's a really nifty syntax. So instead of this, we're gonna go 4, and we're gonna do the the brackets again, but not let it autofill, all of that. Let piece where whatever we want. We're gonna call it piece of my body. Exactly. You've got one too many, close brackets there. And then you do squiggly brackets, and we enter into the code block. Piece is an is a variable inside of this loop, which will change its value for each iteration. So I just think this is yeah. I mean, it if you if you hit tab now, it seems to have already, that code's wrong. I could ain't gonna do anything. I mean, we'll leave it there as, like, as like, you know, we'll base something on this. This is not right. This is saying if if my head is in the spot of this piece, which also includes my head, make everything foresight. This just won't work. It will mean no isn't a viable move and it will keep it will keep sending you know, it will send it, I think, an undefined and then we'll die. So this is wrong. But just before we jump into the logic here, so this is another way to write a a loop in JavaScript. And another way you can do it is called a for in loop where you change the word of for in, and then the value of a piece will be the index value. So what we could have done there, an exact copy of what was above is saying for let eye of my body. And then I will automatically increment each time. It will give you the, in sorry, in my body. And then I or whatever variable name you give it, will will be a will be a number that goes up. The way I remember the difference between of and in, of object in index. I love that. Yeah. So, we'll we'll use the the for of loop. I think it's really nice because when you start to nest them, you can be really descriptive about what that value means inside of the loop. I think it looks nicer. It's a little more concise. But there you go. You learn about JavaScript loops. And there are obviously other loop types as well, but these are the more common 3. So what do we care about when we're looking at our body? Well, for every piece, what we wanna know is, is it one space away from us in any direction? That's all we wanna know. And if that is the case, we cannot move into that position. Even that strictly might not be enough because oh, no. It will be. That that will be enough. That is groovy. So what we wanna check is is our head equal to x? So is it on the same is it on the same x plane? Oh, no. No. Sorry. Inside of this initial if on 95. We're we're gonna alter 95 because 95 doesn't work. So we wanna say if it's on the x plane, but my head dot y is piece dot y minus 1, that means we are one space sorry. That means our body is one space below us. Down goes off the table. So this would be whatever the line is where we remove down. Game what's it called? Yep. Yep. Let's go grab that. Yeah. It is move is move safe. So if it's 1 under us down, down goes off. So is move safe dot down equals force. On the same x plane, hang on a moment. But the y for on the same x we're gonna fiddle with these till they work. This this bit always gets me. I think I might have just done that wrong, but we'll find out. Oh, it's part of the adventure. So, Hannah, if we're on the same x and it's below us if we're on the same Do you need to draw this out? Yeah. Maybe. Maybe. Let's do it. Let's grab, let's grab some It's bothering me. If we're if we're on the same x plane, but it's 1 below but then we're not on the same x plane out. But it's y. I wondered what tool you you were gonna grab there. We'll see if this works. I might pull up some random drawing that I've created. I bet you I am gonna we're gonna see what magical drawing that I have up on the screen here as this loads. What? Can you back a table in there? Because that's all we need is or a Google Sheet even, to be honest. Oh, that's a good idea. Let's do a Sheet sheet. Okay. Let's do, sheets.new. Sheets.new. Sheets.new? Yes. What's happening right now? What is slides.new, docs.new, forms.new. I love it. That's fantastic. Okay. Right. Zoom in. Zoom in. Zoom in. Zoom in a ton. Let's go zoom in. Oh, no. Wrong one. Let's go zoom zoom zoom. There we go. And and let's just do, like, we don't need 11. Right? We could just do, like, a 4 by 4 for the sake of argument. Like or up to 5 by 5 if you go to Ian and you go down to 5. Alright. And I do feel like this visualization is really helpful when you're doing this, like, genuinely. I think this is, like, a process that is useful for anybody to do if you're considering doing this. So draw, like, a 3 parts draw, I don't know. Yeah. Let's just yeah. Yeah. Perfect. Right. So what we want is if you go if you go right one oh, what's happening? What what we're doing? Just give me one second. This is just a small one to do this. Hold on. Hold on. There we go. Great. Hang on. Hang on. I think I think we needed to do it. Do a little more than that. And I would I would leave them as x's so we can notionally talk about the head. So do do maybe, like, make it like a like a u shape. So also fill in b 5 and b 4. I'm gonna you know what? I gotta stop with this x. The the color x is I I I I yeah. Oh, no. Actually, hold on. I could just literally copy and paste. Come on, Andrew. What are you doing? There we go. This is how we want it. Let's assume that b 5 is gonna be the head. Right. So the first thing we wanna do is to say, let let's try and do the left position first. Yep. So the x is the same and the piece is on x minus 1. So that's that's the first one we're we're gonna do. So x is the same as my piece dot x, but but the my piece dot. Oh my god. What's going on? So this is gonna be Sorry. Sorry. Sorry. Sorry. Sorry. The y is the same. Yeah. Because we're all because we're on the same level. We're on the same height. That's what was getting me. So if the y if the y is the same, the x is minus 1, you remove left. So let's do that first. Alright. So if the y is the same, x minus y? Yeah. Yeah. Yeah. So so let let's do the first if the first half of the statement, let's change those for y's. So if the y is the same, but the x is piece x minus 1 remove left. Love that. Okay. And and it's exactly the inverse for right. So x and sorry, y and y, perfect. We we replace those, but this time it's gonna be plus 1. That's all it was. We're we're just struggling struggling with that. So just to be clear, sorry, those first two still wanna be you need to basically inverse all of them. So y Correct. There we go. And then we're gonna return off that will be right. Yeah. And I I don't think we need I'm really glad we pulled out that tool, but I think we can basically invest them now. So copy and paste both of these. So next thing we're gonna do, not like that, is if the x is the same, therefore, the the column we're in is the same. That was it. But the y piece is minus 1, then it's under us. So we're gonna remove down. And, yeah. Exactly. Xxy y. There we go. That took us that took us a hot minute, but we got there. So that should now prevent us colliding with oh, hang on. Hang on. Why? Oh, no. It's fine. On that line 94 piece isn't, like, white, so I thought it was unused. Oh, no. It's because I added an s in there. There was a there was an error there. Just because I'm doing the Windows key save. No. No. No. Peace is declared but never used. I don't think that's right. Oh, there it is. It caught it. There we are. There we go. Cool. Oh, but we got a formatting issue? Hold on. So we can No. It was a it was a formatting issue. No? Hold on. Nope. We're still getting a bug here. Someone watching this right now is screaming. They're like 100%. A 100%. I feel like I'm missing it. Scroll to the top of the arrow. The top of the arrow is gonna tell you tell you what it is, but we're not at the top of it yet. Keep going all the way up, all the way up, all the way up. More more more more. Oh, okay. Less. We went too far. We went too far. It happens. Hold on. There we go. I just went too far into the first interpreter. Alright. Board meeting No. No. We sorted that. That was before now. We did that. Oh, it's this one. Hold on. Hold the phone. Close u. Wow. Capsule consoles. So many consoles. Formatting failed with message. What? And go have I missed something pretty obvious here? No. I think I think that was the that was the issue for let piece of my body. Hey, bro. Yes. I think it was just that s in there. Let's try running it again. You can do it. You can do it. I'm getting a hunch it can't do it without some intervention. What have we done? Is it because we do need to declare this let on my body? Oh, yeah. Maybe that that was the grape last time. Kevin lied. There you go. You lied. You said I didn't have to let it. No. I literally did earlier. That's what happened with board width. I feel like it's it's a useful thing to blame other people for your coding mistakes. I feel like that is what it is the sign of a true a true professional. The professionals we clearly are. Yeah. As I as I went to Google to search how to do a loop in JavaScript. What's what's happening right now? It's all taking a little long. Can you can you refresh the web view at the top? Yeah. Let's do that. I feel like that's k. We can close this out for now. Yeah. Yeah. Yeah. Let's see what's going on. I don't know. Yeah. I don't know what's happening. Maybe repllets happen a day. I mean, this is the thing. Right? It's like we are dealing with a cloud ID, so it does chug sometimes. But Absolutely. The REPL is booting. It's doing its thing. It is. I will say as much as I love cloud IDs or cloud development environments as they're trying to brand themselves now, I use Gitpod for ages, I use Codespaces for a while, I have moved back to using a desktop editor for things like this. Like, why am I waiting for my editor to get its act together? You're not wrong. So so yeah. There we go. And the GitHub CLI makes it really, like, quite quite nice to to move between PRs as I'm reviewing stuff. So, alright, great. Let's rerun the game. So now it should not die from its own body. So in theory in theory, for a one snake battlesnake, I think it's gonna have a yeah. I think it's gonna have a decent life. Look at this. We broke a 100. There we go. This is amazing. Wow. This, okay, 170 moves. Let's see what ended up doing it in no. It could be health or it could be eventually, it had no viable options. You know? Let's see. Yep. So it's doing all the things. Not running into its tail now, which was entirely possible with our previous code. It's not getting bigger because it's not getting food. So now it's going in that circle because it's avoiding its body, and it's avoiding its tail, and it's avoiding its neck. And so I think he might starve out just because he's getting stuck in that circle. Oh, no. Poor buttersnake. Eat something. Look all that food around you, mate. Dang. So, yeah, that's the interesting thing is, technically, he got into that loop where, like, technically, he was right, but we didn't say if you do this more than once, get out of it, which is a thing you can do. And what that, chicken or sorry. What that snake did is a is a a very famous move in the Battle State community called chicken snake. And chicken snake used to, like, go into a corner and would just chase its tail and wait for everybody else to die. And it would do really well. Yeah. And you you basically, you are you either other everyone has to die before you or you have to hope food spawns in in well, it it won't spawn where you are though, will it? It won't spawn in a position where a snake is. So actually It can spawn, like, right where oh, you're actually not wrong. That's absolutely right. Yeah. Interesting. Okay. Cool. So step 3 is a pretty, a pretty good evolution of what we just did there, which is up until now, we have a single battle snake on our board. But in reality, battle snakes will not be alone on the board. There will be at least one other snake on the on the board. And our job is not only to avoid ourself, but to avoid every snake and all of their body parts. Thankfully, we are effectively gonna get to get gonna get to, like, work on this work on this. And in fact, Andrew, I don't think we should copy and paste it. I think we should adapt this because our snake is in the snake's array as well. The might the me, whatever it's called, is again a convenience like the head to just give you the first item. So there's no point doing a loop over all our parts, then a loop over an array which includes us. We should just adapt this immediately. Okay. Great. Let's do it. Alright. So let's go yeah. So we've already defined here. So we've got our opponents defined, and we're gonna do that same let again. Right? Like, we're gonna do our loop with around our opponents, or do we wanna remove ourselves from that first? You know or I mean, this is I mean, it's up to you. Right? We can either, yes, remove ourselves from it, run the loop again because who cares? You know, it's just repeating logic effectively. Or we can adapt what is written above in that kind of 90 to a 110 line range to just be the loop. Oh, I see. Okay. Okay. So where where should we start? Us. So we we know here based on line 116, which we should probably comment out because it doesn't have a let in front of it, but we know that game state dot board dot snakes is an array of all snakes including us. I believe we're always first. So let let's create that variable above this loop, first of all. Alright. Let's do that up here. I see. I see what you're saying. Now when you said adapt, I was like, is that not what I'm doing? Alright. So let's define that. Yeah. Yeah. We we don't need to build on top of what we've already done here. In theory in theory, we can also do a way above with, the avoid our neck logic that it came with because we're gonna avoid our neck by virtue of this. Let let's leave it for now. Let's do a little bit of cleanup at the end, I think. So now we have it's not even opponents. I don't even think that's the right name. It's snakes. These are the snakes, and it includes us. We have a loop that will go through our body and do the check. And, basically, all we wanna do here is write a full loop above it, like surround it in a further full loop, and we'll do the same thing. So we'll go let, snake singular because each iteration is going to be a single snake of snakes. We'll do the open squiggly bracket here on line 44, remove that exactly input precisely, and we'll put that outside the outside of the other one. And all we wanna do here is instead of my body, it will be snake dot body. Oh, Kevin. It's like you've done this many times before. That is just like It is. That is beautiful. So in my hearing and the We we don't know it works yet. No. But I like aesthetically, it's very pleasing. Thank you. You're welcome. Thank you. So now in theory, we're running the same loop for all of for all the snakes. Right? Should we test it? Yeah. Yeah. Let's test. Alright. So let's I'm I was wrong. We do need to stop and start it. So let's see. This should work fine. Perfect. Okay. Let's head back over here. Gotta do a new game now, because we What? Are actually gonna add another set. Of course. Of course. We may as well use one of these bot snakes. I love that scared bot just disappears in this dark mode. In dark mode. Just not there. That's fine. It doesn't matter. We're gonna add him, and we're gonna add in ready, set, and battle snake. Right. Alright. Let's add that in there. Alright. And so we've got a game running now. Now this is another thing about playing this is, like, you can see a lot of these things from your logs. So if you're ever playing BattleSync competitively and, like, there's a competition, like, it's almost like you have to close your logs. Otherwise, it gives too much away. It's like you know Woah. What happened. The other the other nice thing about this is, of course, we get the whole board state every time. So as we start to integrate this with Directus, we can we can consume and store as much of this as we want. Let's, let let's play it. Yeah. Game over. 101 moves. Let's see what happens. Okay. So avoiding body, avoiding other snakes as well. Oh, I think I know how this is. Yeah. I think I know how it's gonna end for this snake. Yeah. Me too. Womp womp. Womp womp. Womp womp. And look how close the food is. Boom. Game over. So it can can we do a rematch? Can we get something hopefully a little more vibrant? Let's do a rematch. Let's see what happens here. Yeah. And we'll play it just so that we can see it in real time. And and this way, you can actually see this What is going on with our snake? Why is it happening? Our snake actually oh, there's an error with our snake because there was a viable direction there. It was to go right, and it did not. No safe moves detected. So that makes sense. So why was it no safe moves? So we have it set that it's not going to go is that it's not gonna go no. Because he's been going up there. So she should just the final move? He should have gone right. Yeah. He should have gone right or he should have gone wrong. Loop. So our logic is wrong. Let's take a look at that. But but and right was the only move. It couldn't go up based on our heuristics. Even though, in theory, to avoid our body, we can remove the tails. We can remove the final piece. But what's interesting is he was doing that. Like, if you look at this, so he was here and he was going here. So, technically, when he was in this spot, he could have actually done this again. Right? Oh, yes. So he could have gone up or he could have gone right. Of course. Interesting. Okay. But let's go to our code and see why that didn't so why did you decide? It's because of the other snake. So it is it does have something to do with the fact that he was right next to a snake, and so he didn't think what about oh, no. No. It's right. My head is still correct. And we wanna be checking the piece of every snake. P sex minus 1, p sex plus 1. Yes. That's right. I feel like it's because we do actually need to bring this logic inside because we're running these this and then we're running through this loop. It shouldn't make a difference, though. So? If it's set force once, it's we never set anything's true. We're never undoing the work we did. We're just kind of repeating it. You know? Yep. Okay. So I guess my head is in there. We don't need the my body anymore. We're not referencing my body anymore. Sninks does include us. Right? Yeah. Well, let's let's figure that out. I think Let's I think it does. I think you is the name of the the object as a convenience. Snakes snake squad. No. That's not important to us. No. That's the snake squad. That and then board. So, yeah, you are getting snake 1, snake 2, snake 3. Can we see the description? Snakes. All snakes on the game board, including yourself if you have not been eliminated. Yep. So it should be working. You're right. So what is going on here? So for snake of snakes, that's good. Again, people watch it people watching this on director's TV are gonna be like, god. It's so obvious. This is the thing. Well, let's hold on. Let's remove some some let's comment out some I mean, sure. Sure. But, I mean, that's literally gonna matter for anything. But okay. So if my head and let's go back to our let's go back to our sheet, drive. 94% of storage used? Yeah. I should probably, go in here and, you know Oh, not pay not pay more for it. Just act stuff. Yeah. That that's the other solution. Okay. So let's say we have let's let's actually reimagine the board state that we were in I love that. Fine. What one step back? I if it's playing, what's going on here? Oh. And we just need to go one step back. We're at the end of the game. Okay. There we go. Alright. So let's bring this to we will actually bring this to a 10 by 10 board. And let's pull this over. No. No. No. No. No. We we don't need the whole board. We only need, like, however wide. We only need, like, the cluster around around what we're doing. We we don't need to recreate the whole board. Alright. So let's go Hang on. I'll, on my side, I'll take a little a little cheeky screenshot, and I can I can call that out for you if you want? K. If you if you head if you head back. Go back to the game. There we go. Okay. So here's what we're doing. Let's go to our sheet. So we only need 1 like, we we can do it all within here, basically. So put our snake on, a 1b 1, a 2, and b 2. The head is b 2. And then, that that snake's gonna go, a 3. There. Yes. One more to the left. We can stop there. The rest is irrelevant. So that's that's what the snake is doing. And so first thing we're doing is we're avoiding ourselves. So we're saying can't go left. We can't go. The thing that's got me is why is it going right? That is I'm sorry. Why isn't it going right when that is a legal option? Yep. That's the question. The right logic is bust. Okay. So let's look let's pull this side by side. Let's grab this over here and let's go get this over here in our logic and see if we can make sure that our logic addresses this situation. Alright. So let's go here. Okay. So yeah, our first code here, my head, y. Yeah. So my head, y, is currently at On on b. Sure. Yeah. Right. On on 1. If it's 0 indexed in the bottom left. Yep. So we're both on 1. Sure. And the x, there is a piece one step down. Correct. Oh, hang on a moment. I think we've got our things muddled here. If my head y, but x is my so now that's correct. So that that accounts for a 2. Left is no longer viable. Yep. Going down 1, it's the same. We're on the same column, but right, this one is what's got me, but the piece dot x plus 1, but that is not the case. It's an it's an and statement. So, you know, both those things have to be true for it to be met. So if y is the same, but the my head dot x equals piece dot x plus 1, then it's on the right, but it isn't. I don't know what's going on here. My head okay. Well, hold on. So this technically, these are x aligned, but they're y they're not equal for y. They're they're they're y's match. They're wise match. They're on the same column. That means they're the same do you think I've just got the accent? No. No. No. I think that my I'm trying to work this out in my head. So this is Call that x1 or x. No. No. That's yes. Yes. No. You're right. You're right. You're right. You're right. Sorry. Yes. Oh, sorry. X1, x2, x3, x4, x5. Yeah. Y1y2y3y4. Yeah. So right now these are the same x, but they're different y's. Well, the yeah. So if we hang on. Let's let's start at the top. So let's do the left first. So the x matches. We've got them flipped, Andrew. We flipped them. Did we? Yeah. Okay. Look, the y is the if the y is the same no, we haven't. We got it right. So if the y is the same, let's say let's take, b 2 and c 2. Right? Yep. The y is the same. Yep. So that they they exist in the same yeah. But the x is minus 1. That means underneath. No. That means To the to the left. Something on the left. Fine. Then y is the same once again. They're in the same row. Are you sure we haven't flipped them? I I think we might have hold on. So okay. My head y, my piece and piece y are equal. So yeah. You're right. These 2 are technically there, and then my head x, so this is my head x, piece x minus 1. So, yeah, this removes left. That that totally makes sense. So we're good there. So next up, my head y equals piece y. Okay. Well, what would apply here, I guess? What would apply to this one right here? It would have to be on the right. I think we got it right. I don't know what's going wrong. My head x. Okay. So right now, this is they are the same for these are the same for x. So my head equals piece x. My head y equals piece y minus 1. Down is false. Yes. That that's right. That's good. My head pieces We flipped them. We flipped them. And what's getting me is the 1 on 1 on line 107. So my head dot x means they are in the same it means they're in the same row. Yes. It means so it means they're in the same column. It means they're in the same column. Yeah. Right? They're in the same column, but the y piece is minus 1. They're in the same column, but the but the y is is 1 to the left. They're in the same column, but the y I I'm struggling to visualize that based on our head. They're on the same Okay. Hold on. So my head ached I think we've, I think we've muddled these. I think I think, yeah, these are wrong. And I know we we know this and the audience like, yeah, it's wrong. And here's exactly how, But, I don't think it's as simple as comparing the y's and then comparing the x's. I think only one piece wants to be I think the Right. So can you just put, like, an a in d two for us? So we're we're gonna test no. No. No. A d 2. So so so so we're gonna test h versus a. Right? So the y is the same. The the y the y is the same. The x is plus 1. The the y is the same and the x is plus 1. Yeah. Right gets removed. So right yeah. Right should definitely So but at least one of these is wrong because we're removing too many options. Yep. If if x is the same, so they're in the same column So let's say y here. So these guys are in the same column. But the y is minus 1. Then we're gonna get that here. Fine. Then the y's are the same, the x is plus 1, then we're going right. Dude, this looks correct to me. It does I don't know what's going on. I okay. So I feel like this we've worked through this. I do think it must have something to do with this logic then. Have we screwed something up here? No. I don't think so. If my head equals 0, head x equals 0. It's anywhere in that line. You can't go left. If it's at the board if y is at the height, you can't go up. So here, this was at the height minus 1. So wait. What is height returning? Am I wrong? Have we changed the rules, and is height actually returning the height of the board and not 11? Oh, no. No. Returns 11. Oh. Oh. Wait. No. That No. That's correct. That's correct. Board width minus 1. Yeah. So that's that's fine. That would be the one that would have mattered that it would have removed the right, and that logic makes sense. Yeah. It's not there. Yeah. This logic is all correct. Let's run another game and see. We are. I am full on at the point where I'm just gonna find a snake because I know I know it's less fun. I know it's less fun, but I just, but I also I also don't don't fancy doing this forever. Let's take a look. K. I wanna see how this game plays out if something similar happens. K. Why did you run into his head? Why was that a safe move? Okay. What was the logic there? Did you have no safe moves again? No. So here, he thought he could go right. So oh, no. Because there. Okay. That makes sense. That tracks because he wasn't directly next. So there's no look ahead right now. So this takes So that's so so that'll happen sometimes. Yeah. So that tracks. That that game worked the way it should have. There's no buggy things there. It worked the way it should. Let's see if we can get another game because I feel like maybe that is it. Like, our code is right and there was just some sort of a glitch, but I don't know. K. So he's going in the circle. He's going in the circle. So he's gonna starve out anyways. We're not gonna get to see much there. Anyone? That's so weird. Can we can we run it again and again and again? So he's not making the same mistake again? I don't I don't like that it's just doing that. That feel it feels weird to me that it keeps chicken whatever you call it. Chicken Chicken snaking? Chicken snaking. That feels very odd to me that it does that consistently. Okay. So he's not doing this as well. It isn't. Okay. Fine. Okay. And, again, so this ish situation, was that a safe move? So he's gotta finish the game. Come on. Come on. Go back. Okay. So this does that make sense? Yeah. Again, this one makes sense because he didn't see right? So every one of these games, it's just the one game that was the one that was very strange. I think that there was some sort of a there was some sort of a an issue there with the actual way that it was reading our logic. I I hate seeing bugs that don't get resolved, but it seems like it's not happening again. It seems to have been okay since. Yeah. Well, let's try again. Maybe Okay. So why did he do that? So he shouldn't have done that either. Okay. So here's the thing. So why did he do that? So right. Right. Right. Game over. So he should not have gone right there. Why did he go right? Why did he think right Something's wrong. Was a safe move. Okay. So let's look at this again. And we're gonna have these guys here. So let's go, I'm actually gonna do color with these this time because I feel like that will actually help us. And then we'll do here and then we'll do him gray green and gray, green, green, green. That's all. I'm literally this is driving me potty. It's alright. This is the joy. I and, we'll fix it in post. Right? Yeah. We'll fix it. We'll fix it in post. Okay. Okay. Do do do do you want them to know what lovely editor, Nat? Let's jump cut here to the point where we fix it, and then we'll explain what went wrong. Yeah. Welcome. Welcome back. We worked it out. Andrew did a lot more like debugging, adding print statements. Andrew, do you want to show the code? And for those of you who are, let's zoom in like many, many, many times over, what was the issue? I'm yeah. But but what was the issue? So the issue was we had things flipped with the Yes. We did. Actual thing that we were removing. And the way that we discovered that, and this is kind of an interesting way to do it. I actually, like, find this as a really good way to do any debugging because I threw a console log into each one of these pieces with whatever we had been removing. So, initially, we so let's, like, put this back to what it was before. Originally, this was is moved left, right, down Down down and up. Yep. And so, originally, we had this. And so I wanted to look here and see where, where we sort of messed up here. And so if you look sort of in this log so, and I think this is the latest game. Is it the latest game? No. We ran it once more since. We ran it once more since. Did he did he go out for the same reason? That's my question. Let's find out. Oh, no. Because this was what he was buying correctly. So Yeah. Did I see But but but but but we we we don't necessarily need to find it or or replicate it. But, yeah, you you basically post it in a in the state just before you died, just before it died. Yeah. And then where and then did kind of what we did with the spreadsheet again. And, basically, we had it flipped. So up and down needed to flip, left and right needed to flip. And then our snake has also been a lot more interesting as a result. Yeah. So technically, like, even with bad code, it worked. Change it back. Change change it back now before Yeah. Before we forget. So let's go and I'll actually go in here, and I'll remove these console logs because they're false now. Let's go right. Mhmm. And remove this one and left. And and I feel like this is kind of the fun of this game is it's like you orienting yourself is also, like, I think it's part of the challenge here. But now, you'll see here so, like, well, in that last game, he didn't follow the rules like we thought he was going to. Now he, she, they, if we go back in here and create a rematch, we're going to see that, like Kevin said, this game is far, far more interesting now. And so here so now he's not constantly going in that loop. He's not running into these other snakes. And there we go. Now we're starting to get some wins. Yes. Now I think that might be where we call this snake because the reality is this snake's doing pretty well. For the extent of our series, we don't need to do much. However, there are some other end statements here. Did our snake die or did no. No. No. No. No. That was good. Nope. We did the right thing. He he just did head to head. So head to head is the other thing to consider. If you collide with a snake head to head in any configuration, right, if you both enter the same square, the snake with the lowest to health, I think. Right? Yeah. Or the shortest snake? Yeah. No. Which one? I just gave you 2 options. Oh, sorry. Shortest so it's it's the snake snake with the it's the snake with the lowest health, I believe. No. Wait. Hold on. Let's talk a little bit. The shortest snake. Always consult the docs whenever you're not sure. And so and so what you will need to do then is also look around the heads of each snake and have an exclusion zone of one one valid direction in any way they can go. You can also remove the tail as well because next move, the tail is gonna have moved out of place. So these are the little bits of logic you can do. The longer Longer about the snake will survive. Shorter will be eliminated. If they're both the same length, they both die. Fine. Fine. So there is more logic we can add to this, Nate. But the reality is it's gonna perform pretty well. Apart from head to heads, it should perform or running out of food legitimately because it currently is just hitting food randomly. It should perform pretty well. Good enough for ready set battlesnake. But, yes, in reality, you'd also have an exclusion zone around heads, and you would be able to take the last item of each snake body and remove that from your from your checks because it's going to move out of the tail space. Or rather, the tail will not be in that space anymore. So you can stop. Ahead might be, but the tail won't be. Sick. Okay. Ready, set, battle snake episode 1. I think the last thing I'd love to do before we just say bye and and stop recording, shall we just do a little bit of cleanup on this on this, file from the top to the bottom, make it feel good, and then we can wrap up. And next episode, we will take this snake and see if we can run it inside of a director's project. Amazing. Alright. So, yeah, let's clean this up. Let's leave those comments in there because I think they're great. Let's leave that step 4 in there as well. So let's go up here. Now we do need to add a couple of things. So let's do, let's call this Directus TV as our author. And we're going to leave our stuff until next week, I think -I think so. - for our default. But, we've got that in there. This is all good. This is all good. Do we wanna leave this or do we wanna move this inside? What do we wanna do, Kevin? What do you think? What do you mean inside? Like, do we wanna deal with moving this logic for the head and neck inside of our inside of our other logic? You can straight up remove it. Leave the variables at the top because my head and my neck might are used. My head is used. My neck is not used. We can remove those. They're they're captured in the other logic. Yep. You're absolutely right. Yeah. Okay. So we can remove and we can remove my neck because we don't need that anymore. And This is all good. I actually think we should put all of these together. Is there a reason that we should have our board width and height as let and our my head is const, or should we just have them all the same? I feel like they should all the same. It literally makes no difference. Like, in reality, I would make them all cons because we don't reassign them. And I think inside of the 4 loops, you can also use const instead of let, if memory serves me right. I feel like there's probably a way to select all of the lets in here. Command d. Command d. Or control control d. Control d. There we go. Let's call these all const. There we go. Lovely. And the reality is we're still being overly verbose here. Like, line 79 could just be put inside of line 81. Like, we don't we don't need to be declaring all these variables, but I think this is fine. We're gonna move it over next time. I think we said we're gonna we're gonna try using direct automate to make this work, which I'm really excited about. There are some interesting quirks. I'm not sure I've quite figured out yet. We have a a pressure based rate limiter that I think we might hit, so we'll we'll have to explore that. But we'll we'll see. Yeah. But I think that's I think that's everything. I think we've pulled out any unnecessary variables in here. I think this is looking pretty pretty good right now. Sounds good. Great. Hit Save, and let's let's wrap up. I think this was a really nice first episode. Very happy we cut that that bit in the middle of working out what we were doing wrong. So thank you so much for joining us for the first episode of Ready, Set, Battlesnake. We won't see you next week. This is every 2 weeks. So if you are watching at the time it's coming out and not binge watching at the end, you will find the next episode right here on DIRECTV in 2 weeks' time. I've been Kevin. I've been Andrew. There we go. And until next time, you ready for the catchphrase? Ready. Set Battle Snake. We'll get there. We've got 4 episodes. We've got 4 episodes. Bye. Bye.",[161,162],"bd1b80bd-7270-4544-83b4-7f4dcf1816ab","4778071f-905a-4fef-84ab-db818203e4e7",[],{"id":134,"number":135,"show":122,"year":136,"episodes":165},[138,139,140,141],{"id":139,"slug":167,"vimeo_id":168,"description":169,"tile":170,"length":171,"resources":8,"people":172,"episode_number":175,"published":176,"title":177,"video_transcript_html":178,"video_transcript_text":179,"content":8,"seo":8,"status":130,"episode_people":180,"recommendations":183,"season":184},"automate","925447352","Kevin and Andrew migrate their snake to run entirely in Directus Automate's low-code builder Flows. Is this the right way to run a Battlesnake? Tune in and find out.","37419656-e608-48d3-8d79-6fdb4fbcfac2",110,[173,174],{"name":151,"url":152},{"name":154,"url":155},2,"2024-04-04","Automate","\u003Cp>Kevin: Hello, and welcome back to we've got a catchphrase, Andrew. Are you ready? Ready, set, That's easy. We got 4 more We got 4 episodes. Exactly.\u003C/p>\n\u003Cp>We're we're we're we're cutting our losses for this one. In this show, we are continuing to build a Battlesnake, and progressively enhancing it, using Directus and Devcycle. My name is Kevin. I work at Directus, which we'll be using a little bit today. And My\u003C/p>\n\u003Cp>Andrew: name's Andrew, and I work at Devcycle, which we'll be using in a future episode. And this is Battlesnake.\u003C/p>\n\u003Cp>Kevin: Yeah. Give us the give us the 90 seconds for people just dropping in now. What is Battlesnake?\u003C/p>\n\u003Cp>Andrew: Alright. Battlesnake is the way you wish that you had learned how to, program in a new framework or language from the very beginning. And now as a senior developer, Imagine you were playing the old classic snake game where you go around a board trying to eat food and survive. Now transform that into a programming game where you have a web server that's communicating with the Battlesnake game engine to communicate through code and tell your snake whether to go up, down, left, or right, and to try and survive as long as possible.\u003C/p>\n\u003Cp>Kevin: Yeah. And we do that by implementing a web server, which implements the Battlesnake API. We've already had one episode of Ready, Set, Battlesnake. So we're not going to recap everything we did. If you are still a little fuzzy on how this works, it will probably become clear.\u003C/p>\n\u003Cp>But if you're completely fresh and you're like, I'm lost, go watch the first episode and watch a struggle through building the Node. Js starter snake. But today, we're gonna further build on top of that snake. Last episode, we built a battle snake based on the Node. Js starter, and we implemented here we are.\u003C/p>\n\u003Cp>And we implemented some logic. That logic, what what did the logic do? The logic stop listening dying by going hitting into walls. That's good. Then we got the snake to not hit itself or any other snake parts, because that also is instant kill.\u003C/p>\n\u003Cp>So that's all our snake does. Right? Yeah. Yeah. That's all our snake does.\u003C/p>\n\u003Cp>And we're probably not gonna make our snake much more clever today, but we are now going to introduce a new tool into the stack and get it out of Replit and into another environment. Anything else you think that's important, Andrew?\u003C/p>\n\u003Cp>Andrew: So the great thing is you can follow along with kind of all of the base code for all of this over on the GitHub repositories that exist around. Snake. So if you're watching this and you're like, I want to do it in my own language or framework, you can go and check out all the\u003C/p>\n\u003Cp>Kevin: awesome Angular projects\u003C/p>\n\u003Cp>Andrew: that are over there.\u003C/p>\n\u003Cp>Kevin: So actually, let's take pause for a moment because this is important. What is a Battlesnake? So you have to implement a web server that has a few specific route handlers. The a few specific endpoints. Firstly, this just route route.\u003C/p>\n\u003Cp>Right? And all this one does is it returns information about the snake, metadata about the snake. And we'll play around with that a little bit more today. The mode then we have the start endpoint, which, Andrew, I think that's the before the first move in any game. Right?\u003C/p>\n\u003Cp>Andrew: So we got our start end point, which is great. We've got our root end point. What is the kind of most important endpoint of all the endpoints though, Kevin?\u003C/p>\n\u003Cp>Kevin: It is this move endpoint. As we discovered last time, the move endpoint, every single turn of every single game will receive a request with the state of that game and the board, which includes every other snake, all the hazards, all the food. You have 500 milliseconds to respond with a string up, down, left, or right. And that is this is the most important endpoint. But you there is also an end endpoint, which is what you would potentially use to, like, wind down any resources or, you know, do any analytics storage, you know, and you just do it at the end.\u003C/p>\n\u003Cp>So we have these 4 endpoints that we need to implement. Move is is kind of the most critical. I think only move in the the root are required for any given snake. So that's worth noting because that's gonna be important. So what we're gonna do today, we are going to be building this snake inside of Directus.\u003C/p>\n\u003Cp>If you've not heard of Directus before, I hope you have because you're watching this on Directus TV. But Directus is is a back end toolkit to build applications. It has 2 parts. One part which is all around API generation from your database and asset storage, and the other side, which is a web application to kind of work with that data. There's a whole bunch of other stuff in there.\u003C/p>\n\u003Cp>So we're gonna be implementing a Battlesnake inside of a director's project. So we'll we'll we'll kinda see how this shakes out over the next over the next little while. Now originally, until about 5 minutes before we started recording, I thought that we would perhaps use, Directus' automation UI tool in order to build our snake because it is it feels quite viable on the surface. It does. And then I realized it ain't it ain't viable.\u003C/p>\n\u003Cp>And the reason it isn't is because one of the triggers that can begin a flow is a webhook, an inbound webhook, but it is unique, and randomly generated at the time the flow is created, which means we can't implement a root endpoint and then slash move, slash start, slash end. And that's gonna be a challenge. So rather than just trying to to power on through that, rather than sorry, rather than just trying to power on through that, we instead are gonna build an extension. Flows is effectively a UI layer or a UI version of, hooks of hooks. So, you know, we can we can do it in code.\u003C/p>\n\u003Cp>We also have endpoints, which is probably more suited for this where we can literally create complete endpoints, and this is a no doubt. So we'd probably lift and drop a lot of our logic. This might end up being a bit of a lighter episode, but there would be nothing wrong with that. But I've done the thing. I've jinxed it.\u003C/p>\n\u003Cp>Andrew: You have. I did it last time, though. So in fairness, we're we're each one in, and and so I feel like Indeed. This is way to do it. Indeed.\u003C/p>\n\u003Cp>And so we're also we're gonna be implementing some some, like, very like, this whole rate limiting thing has caused us to have to think about a lot of other technologies as well too. Right? So it's not just this concept of of sort of how we're gonna implement Directus. It's also\u003C/p>\n\u003Cp>Kevin: You're right.\u003C/p>\n\u003Cp>Andrew: How it's gonna be able to interact with with the Battlesync platform. Right?\u003C/p>\n\u003Cp>Kevin: Yes. So here here's the plan. Here's what I think the game plan is. We're gonna spin up, direct us locally using Docker. And that is an environment in which we can build extensions.\u003C/p>\n\u003Cp>And we could then just deploy it somewhere and be happy days, you know, deploy it on some some web service, or we could just, like, open up a tunnel to our local machine. And ngrok is kind of the tool that we've as as developer educator types have have historically jumped to, so I think it will be the tool we use today. I also found out today I'm apparently still paying for it on a grandfathered plan that gives us the pro features. So, you know, I think it's one I might wanna\u003C/p>\n\u003Cp>Andrew: Let's do it.\u003C/p>\n\u003Cp>Kevin: Get off the card, but I'll use it today.\u003C/p>\n\u003Cp>Andrew: I saw a I saw a really great, idea from someone recently just to, like, automatically, not only list all of the subscriptions that you have, but actually unsubscribe you from something if you haven't accessed it in a while. And I was like, that's a brilliant idea because I have so many things to subscribe for that I'd like to remove my credit card from.\u003C/p>\n\u003Cp>Kevin: Yes. But I'm happy in this moment in this moment that it's working. Now one thing that's really nice here is the extension entry point. If we look at this, example, the way it's built is you give an endpoint an ID, which becomes the kind of root path. So this would be like our director's project slash greet, and then you can create the root, the slash move, the slash start, the slash end, and so on.\u003C/p>\n\u003Cp>So this part should be pretty straightforward. Let's let's see, and if the docs end up being misleading, that's literally my job. So it's it will be incredibly self inflicted. So we'll we'll see. So we're gonna start by following the Docker guide for getting started with directors.\u003C/p>\n\u003Cp>No. We're not. We're gonna look at the self hosted quick stuff, which which\u003C/p>\n\u003Cp>Andrew: is super smooth. It is a very smooth Docker process, I will say. Like, setting it for the first time, it was it was chef's kiss.\u003C/p>\n\u003Cp>Kevin: I like it, and it's pretty, we we kinda sometimes refer to it. I mean, we will use different words, but I call it like full fat. This is the full thing. There's directors cloud, which is like, you know, a a managed platform. But this if you self host it, it's full fat, which is really nice.\u003C/p>\n\u003Cp>So I already have Docker running in the background and I already have a code editor here, completely completely blank code editor and a new directory, which I've opened up. So I think we're just gonna we're just gonna crack on straightaway in here. So first thing we need to do is create a Docker Compose file. It's a Docker dash compose dot YML file that describes all of the settings that we have with directors. We may need to fiddle with this later, But for now, docker compose dot yml.\u003C/p>\n\u003Cp>And let's make my font size significantly bigger. There we go. We can copy and paste the value in here. Keys and secrets are meant to be random values. They're used for, identifying, your account in, like, a horizontally scaled setup and the secret is used for JWT validation, and we don't care about that right now.\u003C/p>\n\u003Cp>So we're just gonna leave those. Hunter 2 is when I set up projects my default. So we're gonna Good\u003C/p>\n\u003Cp>Andrew: to know. Good to know.\u003C/p>\n\u003Cp>Kevin: Good to know. Good to know.\u003C/p>\n\u003Cp>Andrew: I'm searching around for past Kevin projects and just trying to break in.\u003C/p>\n\u003Cp>Kevin: Absolutely not. And no. No. That will that will not be good. We're gonna use, yeah.\u003C/p>\n\u003Cp>Sorry. Go on.\u003C/p>\n\u003Cp>Andrew: No. I was gonna say an interesting note here is I discovered with the self hosted version, you can't use a fake email address. You can't use dot test. It won't actually accept it as an email address in the platform.\u003C/p>\n\u003Cp>Kevin: Yeah. Interesting. Interesting.\u003C/p>\n\u003Cp>Andrew: I was when I was building out that railway sort of like starter kit for it, I was trying to just set it up with a test and it would not it wouldn't go. You had to put it in an actual address.\u003C/p>\n\u003Cp>Kevin: Interesting. As in, like, it follows the structure of an email address, but it isn't real because it should do. That should be fine.\u003C/p>\n\u003Cp>Andrew: I think it was because it was a dot test, t, like, t Yeah.\u003C/p>\n\u003Cp>Kevin: But that that's they're legit characters in an email address. So anyway anyway. Things that's important yeah. One thing that's important to know about directors is it doesn't provide a database. It connects to an existing database.\u003C/p>\n\u003Cp>We're gonna use this lightweight file based database called SQLite. We're gonna point it at the file. And on first run, it will create the file for us. That isn't Directus, that's a SQL like database that Directus connects to. So just a little bit of important distinction there.\u003C/p>\n\u003Cp>10.8.3 is the latest version at the time of recording. By the time of publishing, that will not be the case. I think we might be on 10.9 by now. That's pretty exciting. Yeah.\u003C/p>\n\u003Cp>All good Database persistence, file persistence, extension persistence. WebSockets are enabled even though I'm not sure we're gonna use them. And instead of, continuing to talk through every line in excruciating detail, I think we can just write Docker Compose up.\u003C/p>\n\u003Cp>Andrew: It should kick off.\u003C/p>\n\u003Cp>Kevin: Great. It's attaching. Boom. Boom. Boom.\u003C/p>\n\u003Cp>Doing a bunch of stuff first time and running now on local host 8055. So if I hit local host 8055, brand new directors project with randomly auto filled credentials, which are not I think I have to move them out of, yeah, manage passwords, but that's not for today. I bet you that was a hunt to 2 password too. But this is a blank director's project ready to go. So that was that was great.\u003C/p>\n\u003Cp>What else is important here? If I open up oh, I haven't opened up the folder. This is annoying because obviously the terminals attached to it. So let's just, let's just stop that running. Let's open this snake folder here.\u003C/p>\n\u003Cp>I think I might have been able to add folder to workplace. We're here now. Let's just run Docker compose up again. Great. And just refresh that and everything should still be fine and dandy as it is.\u003C/p>\n\u003Cp>Now the first time we ran that, it created some directories here on the side, which I think are gonna be quite small right now on people's screens. But as a database directory with our SQL like database and extensions directory and then uploads directory. Grand. So we have this direct as project and it's great that we, you know, can go in and create, you know, collections in our database and upload files and create dashboards and all of that stuff. But really, all we care about is implementing these endpoints from our Replit and bringing them over.\u003C/p>\n\u003Cp>So let's go back to the docs. Let's talk about building extensions and get that get that game get that game underway. Directus has a whole bunch of extensions. Some of them affect the UI that that web app that we just had open. Some of them are API side like custom endpoints and hooks.\u003C/p>\n\u003Cp>Yeah. As it says here, hooks are similar to flows, but they do not have the UI, which is in the director's data studio. I think to create an extension, we're gonna go I'm sounding a little uncertain. I don't know why.\u003C/p>\n\u003Cp>Andrew: Like No. I love it. It makes me feel much better about about episode 1, now that you're feeling unconfident about the thing that you deal with every day. I feel like imposter syndrome is real, but at least you haven't had to Google how to make a JavaScript loop yet. So\u003C/p>\n\u003Cp>Kevin: Oh, good.\u003C/p>\n\u003Cp>Andrew: The episode is young. Run\u003C/p>\n\u003Cp>Kevin: so I'm gonna run this MPX create directors extension. I'll pick an endpoint, pick a name, directus dash extension dash, snake. Snake. Snake. I it doesn't it doesn't really matter.\u003C/p>\n\u003Cp>We'll use JavaScript. We'll auto install dependencies, and we'll give it a moment to scaffold that extension for.\u003C/p>\n\u003Cp>Andrew: This is a nice CLI. Like, I gotta say, this is a very enjoyable experience.\u003C/p>\n\u003Cp>Kevin: Yeah. There's still work that needs to happen, and it is happening. But, yeah, this boiler plating isn't too bad, actually. I, yeah, I I I quite like it. So inside of our extensions directory, we have this new snake.\u003C/p>\n\u003Cp>We have source. We have this index dot JS. Grand. Now what we want and so this, I think I think this might be available, I think, at directus dash extension dashnet like slash. It's a little bit clunky, but we're not going to use this format.\u003C/p>\n\u003Cp>It doesn't really matter. We're going to use this. We looked at it earlier, this alternate format, where we can do all of the subroutes straight in here. So I think I think I can just copy and paste this.\u003C/p>\n\u003Cp>Andrew: Kevin, is this just is this just vanilla, or is this React? Like, I this looks very clean, but it doesn't Yeah.\u003C/p>\n\u003Cp>Kevin: So this so the front end of Directus is a view app, but this what you're seeing right now and ding, ding, ding, I think you've just spotted it. Endpoints use oh, directors also uses the express router. So it is very much what we did in the first night, which is why being able to lift and drop it is gonna be gonna be quite nice.\u003C/p>\n\u003Cp>Andrew: It's so clean. I dig it. I love this. This is beautiful.\u003C/p>\n\u003Cp>Kevin: Yeah. Thank you. So we have snake slash snake intro snake goodbye. So let's get this running. So we're gonna c d into our Directus snake directory and we're gonna run npm run build.\u003C/p>\n\u003Cp>That's built the extension into a disk directory. And now we need to restart Directus now. I think I think it might be broken, but I think we can get it to I think we might be able to get it to, like, auto, like, load the the changes. What am I looking for? Self hosted config.\u003C/p>\n\u003Cp>Okay. So we are looking for extensions, extensions auto reload. Here we go. So Boolean will automatically reload extensions when they have changed. So we're back to our Docker Compose.\u003C/p>\n\u003Cp>We'll throw that in and make the value true. Maybe that will work. Maybe that will work. So run Docker compose up again. It's now gonna use this Docker compose file again with this new variable.\u003C/p>\n\u003Cp>Let's take a little look through the logs because hopefully yes. It says loaded extension, direct us extension snake. And then the hope is if we run npm run build again, it hopefully would have auto loaded, but that isn't the case. That's fine. That means we're just gonna have a slightly clunky development experience.\u003C/p>\n\u003Cp>So I'm gonna have to control C up enter, you know, off the baton.\u003C/p>\n\u003Cp>Andrew: That's fine. I gotta say though, like I remember like outside of this, I feel like we've gotten very spoiled in the development world now. Like, that used to be just the way that you did it. I used to use some.\u003C/p>\n\u003Cp>Kevin: Dragging files with Filezilla or Cybernet. Yes. A 100%. Yeah. Yeah.\u003C/p>\n\u003Cp>Andrew: I that's so I thought that's how you did it. I genuinely thought that that was the only way to do things.\u003C/p>\n\u003Cp>Kevin: No. Yeah. But not here. Like, this this, I'm not quite sure what the deal is. And by the time this is published, maybe this won't be the case anymore.\u003C/p>\n\u003Cp>Just for the last couple of versions, if you're running under Docker, I've noticed this environment variable isn't working. Our team's aware that's fine. I just wanna try it on the off chance there. Yeah. Whoops.\u003C/p>\n\u003Cp>So if we hit 8055 snake, we get the hello world. If we try snake slash intro, we get the nice to meet you. So these are endpoints that are returning stuff, which means we should now be able to lift and drop our snake pretty expediently. Yep. So what we what are we gonna do here?\u003C/p>\n\u003Cp>We have handlers dot info for the root. Yep. So you're gonna grab that. Return that. So I am just gonna make this slightly more verbose so I can space it out nicely.\u003C/p>\n\u003Cp>Yeah. So, can you bump up the\u003C/p>\n\u003Cp>Andrew: size a little bit too?\u003C/p>\n\u003Cp>Kevin: A little bit more?\u003C/p>\n\u003Cp>Andrew: Of course. Is that any any better? That's that's much better. Yeah.\u003C/p>\n\u003Cp>Kevin: Lovely. Rez got Jason. Yep. Do you need to return it?\u003C/p>\n\u003Cp>Andrew: I I believe so. Yeah.\u003C/p>\n\u003Cp>Kevin: Cool. Nice.\u003C/p>\n\u003Cp>Andrew: Yeah. I think everything you need to return.\u003C/p>\n\u003Cp>Kevin: Whoops. Got a little too a little too direct there. Let's, let's while we're here, maybe let's do some customization of the snake. That feels good. Yeah.\u003C/p>\n\u003Cp>That's good. So head over What color do we wanna make it?\u003C/p>\n\u003Cp>Andrew: Oh, what's the direct is it a direct purple? Is the is the color purple that you're using?\u003C/p>\n\u003Cp>Kevin: It is a purple. And, honestly, my lot are gonna kill me for, like, not remembering it, but I think it's, like, 6 6. I wanna say it's like 6644 f f.\u003C/p>\n\u003Cp>Andrew: I like it. Let's see. We'll find out momentarily whether this is Yes. Yes. That's right.\u003C/p>\n\u003Cp>I got it. 6644ff.\u003C/p>\n\u003Cp>Kevin: I logged into my Battlesnake account. We're gonna look at the customizations. And these are the customizations we can have for our snake. Andrew, you you may do the honors from the ones I have.\u003C/p>\n\u003Cp>Andrew: Oh my goodness. So I'm a big, big fan of mustache snake, but I'm pretty sure that there if we scroll down, there may be a rabbit in here now. I thought I thought we\u003C/p>\n\u003Cp>Kevin: had play around seeing if I could get the director's Bunny to look good in one of these, and I had just I couldn't with, like, the it has to be full width on the left. The Bunny, let's take a look.\u003C/p>\n\u003Cp>Andrew: Do we have a Bunny? If not, I know he exists in the backlog. So we may we may be able to get Bunny out at some point here, or he may be locked down. Either way, if not, if we can't get our bunny Oh,\u003C/p>\n\u003Cp>Kevin: I have 200 I have 200 coins as well, so I can unlock any one.\u003C/p>\n\u003Cp>Andrew: Oh, so what do we see? Do we see any that we really like? So we got chicken. I quite got\u003C/p>\n\u003Cp>Kevin: I quite like this dragon head here. I have to be honest. Yeah.\u003C/p>\n\u003Cp>Andrew: Take the dragon. But is\u003C/p>\n\u003Cp>Kevin: it was really funny. No. They're all 200. I don't see a bunny.\u003C/p>\n\u003Cp>Andrew: No bunny. That's disappointing. So let's go for dragon. I feel like that dragon\u003C/p>\n\u003Cp>Kevin: head Well, hang on. Let's let's look at the tails too. Let's let's, you know, maybe not spend all of our coins at once. True. No.\u003C/p>\n\u003Cp>I think I think the I think I like the heads. Well, yeah, let let's do the dragon. I've done it. Right. So the head we want is dragon, and the tail is So,\u003C/p>\n\u003Cp>Andrew: I mean, let's go I think cosmic horror's tail is pretty fun.\u003C/p>\n\u003Cp>Kevin: I like that. I like that. Okay. Cosmic Chorus. So that's our that's our snake.\u003C/p>\n\u003Cp>And once again, actually, should we get all the logic in? I think we should do this bit by bit. Like, we could now go off and make sure that this works, which is gonna be a whole task in itself. Does that feel okay?\u003C/p>\n\u003Cp>Andrew: Yeah. Yeah. I think let's do that because then once we know it's working, then we can kinda go through. Is that what you're saying? You're thinking, like, let's go in and actually add it to the platform.\u003C/p>\n\u003Cp>I am with NVIDIA because we gotta get we gotta get ngrok set up now.\u003C/p>\n\u003Cp>Kevin: Yeah. So do you wanna explain what ngrok is while I\u003C/p>\n\u003Cp>Andrew: get sucked in? Yeah. Yeah. Absolutely. So if you've never used ngrok before, it is a way to publicly expose your local, development environment to the outside world, whoever you would like to be able to access it.\u003C/p>\n\u003Cp>So if you often use, say, Gitpod or, Codespaces, there's often the way that you can make that public u that URL from private to public. That's fundamentally what this ngrok, endpoint that it's giving you is doing. But you can do it on a on a, case by case basis. And you can kinda keep it in one place, and you have a bit more control over the power of that and who can see it and all the rest of that stuff. It's our and it's really great, especially if you're, like, say, somebody that's building a site and you wanna see what it looks like on mobile.\u003C/p>\n\u003Cp>I think they might still do it. But I know there's some services that I've used that it would automatically generate a QR code from the n grok address so you could just, like, scan with your camera and then view on mobile. Doesn't impact us here, but, really, really good. And ngrok has taken a lot of steps to make things more secure as well. So it used to be that sort of anybody could spin up an ngrok address and there was no issues at all.\u003C/p>\n\u003Cp>And they've added a lot more security and ensuring that the things that need to be in place are. Yeah. And I will say around ngrok, if you are, on a local development environment on a Windows device, so a big Windows developer, there's a service called Laragon, which is like, an alternative to like a LAMP stack. And it has ngrok built in. So in there, you can just click share and then it automatically will, like, push that out to ngrok that you could share with people.\u003C/p>\n\u003Cp>But, yeah, powerful, powerful service.\u003C/p>\n\u003Cp>Kevin: So there's a little bit of a moment here. I'm not quite I don't quite have the memory of how this works. I think it works like this. I think based on what I've read, So you do ngrok HTTP, the port, which is 8055 and then dash dash domain and then your your domain. No.\u003C/p>\n\u003Cp>Reserve this name on your dashboard.\u003C/p>\n\u003Cp>Andrew: You don't need to do that. It'll automatically just generate a random one for you. So just No.\u003C/p>\n\u003Cp>Kevin: Yeah. But I don't want that because I'm gonna have to keep updating it every time it restarts.\u003C/p>\n\u003Cp>Andrew: I think I\u003C/p>\n\u003Cp>Kevin: have I have a static. I have a static.\u003C/p>\n\u003Cp>Andrew: Oh, I see.\u003C/p>\n\u003Cp>Kevin: Like, because I because I'm on this plat this old plan. Right? Yeah. So I wanna work out\u003C/p>\n\u003Cp>Andrew: Yeah. I haven't touched ngrok in in a little while. It's funny, like, it stopped working in that environment. There was, like, they hadn't updated the way that was integrated, and they added an authentication key. And so, it switched things up.\u003C/p>\n\u003Cp>So your guess is as good as mine.\u003C/p>\n\u003Cp>Kevin: We're gonna get oh, alright. Here we I mean, yeah. I suppose I could just do the help, right? So start engrop. That's cool.\u003C/p>\n\u003Cp>Basic auth. Cool. OAuth 2 cool. Here we are. Use it the same time by using dash dash domain.\u003C/p>\n\u003Cp>But, and I thought maybe I, maybe I.\u003C/p>\n\u003Cp>Andrew: What's your domain?\u003C/p>\n\u003Cp>Kevin: I thought it was that. That's why I that's why I paused. So if I look at domains, there it is. Unless I have to do the whole domain.\u003C/p>\n\u003Cp>Andrew: Try the whole domain and see what happens.\u003C/p>\n\u003Cp>Kevin: Because it because it did just say the ngrok.io is only for, like, legacy accounts. Yep. And that was it. Yeah.\u003C/p>\n\u003Cp>Andrew: There you go.\u003C/p>\n\u003Cp>Kevin: Great. So we've now got we've now got engrok rocking and rolling over here. Great. So now what we're gonna do is go over to battles go over to Battlesnake. We'll create a new snake like we did last time.\u003C/p>\n\u003Cp>So this is ready, set, battlesnake. Ready, set, battlesnake. Cake.\u003C/p>\n\u003Cp>Andrew: Yes. That's it. Nope. Nope.\u003C/p>\n\u003Cp>Kevin: K for Kevin.\u003C/p>\n\u003Cp>Andrew: That's it. I love it. Beautiful.\u003C/p>\n\u003Cp>Kevin: So https phzn.ngrok. Io/snake. Engine region, Netherlands or Netherlands? Yeah. Well, Europe.\u003C/p>\n\u003Cp>So yeah. Sure.\u003C/p>\n\u003Cp>Andrew: Yeah.\u003C/p>\n\u003Cp>Kevin: Programming language, JavaScript, and platform Director. Should be\u003C/p>\n\u003Cp>Andrew: there. Directus. Now my question here is, does the logo look good?\u003C/p>\n\u003Cp>Kevin: Oh, let's see what happened to it.\u003C/p>\n\u003Cp>Andrew: So I used the I got them to add Yeah. It doesn't look too bad.\u003C/p>\n\u003Cp>Kevin: Yeah. It's an old it's an old one. So we'll fix it by the next episode, but it's good. It's just colors just colors and details. But yeah, that's that's our little bunny rabbit boy.\u003C/p>\n\u003Cp>Unknown API version. So there was an error. Yep. Even though we copied\u003C/p>\n\u003Cp>Andrew: You should paste it. So what I would say is, I think I think I think I think you might need to just reset. Because this used to happen on\u003C/p>\n\u003Cp>Kevin: Reset reset what bit?\u003C/p>\n\u003Cp>Andrew: Like oh, sorry. Reset everything. So just, like, ping it again. And if it doesn't work, then actually start running, like, restart your direct to server. Okay.\u003C/p>\n\u003Cp>This used to happen with replit all the time is it would get this, and then it would just need a couple minutes to start syncing up in the right way.\u003C/p>\n\u003Cp>Kevin: I don't know what that mean. I don't know what that means.\u003C/p>\n\u003Cp>Andrew: No. It's no. That's right.\u003C/p>\n\u003Cp>Kevin: So if I hit that endpoint Yep. Oh, it still says hello world. Oh. Why? Oh, why?\u003C/p>\n\u003Cp>Because we needed to rebuild the extension, then we needed to restart director. So there you go. That was our little people.\u003C/p>\n\u003Cp>Andrew: What are you doing?\u003C/p>\n\u003Cp>Kevin: No bill. What what what are you doing? Hit ping. Perfect. There it is.\u003C/p>\n\u003Cp>Oh, battle snake. It looks sick. Yeah. I love that. Yeah.\u003C/p>\n\u003Cp>So now we have a snake. Right? But we haven't implemented any other endpoints. So this is the time to do it. Now this is the point where maybe, like, we split out the move logic into a separate file to stop it.\u003C/p>\n\u003Cp>No. Do you wanna know what?\u003C/p>\n\u003Cp>Andrew: I actually wish that the move logic was in with everything, even in the JavaScript starter snake. I understand why it's not, but I actually think it's way better to have everything in one place just so all of your routes are are there and you're you're able to change that data. So I think I think let's do exactly what we That\u003C/p>\n\u003Cp>Kevin: works for me. So there we there we go. And I am just gonna shrink this slightly and shrink this slightly so we can get the replet up and kind of do a little do a little moving from one to the other. Now can I, can I shrink this sidebar down? Yes.\u003C/p>\n\u003Cp>Perfect. And maybe even shrink that. Okay. Rock and roll. Get rid of that.\u003C/p>\n\u003Cp>Oh, and get rid of that. There we go. So first thing we're doing is is move safe? So move, get some data in. Let's console log the request just so we can make sure, like, is it in request dot body?\u003C/p>\n\u003Cp>I I mean, I'm assuming it's in request dot body. Yep. Let's let's just double check that, and just respond with hello.\u003C/p>\n\u003Cp>Andrew: I like that after our last adventure where we had to disappear and through the magic of editing come back, we've decided to now add console logs earlier as we go so we can save ourselves some trust down the road.\u003C/p>\n\u003Cp>Kevin: That's my style. That's my style. Discum. Okay. Experiential.\u003C/p>\n\u003Cp>This is the point where and these these logs are gonna get noisy in a moment. Yep. I may even take them down inside until we need them, and then I can make them smaller still and then I can bump them back up. So what are we gonna do here in Battlesnake? We have a Battlesnake.\u003C/p>\n\u003Cp>Let's create a game, create game. Add ready set Battlesnake standard board 11 by 11.\u003C/p>\n\u003Cp>Andrew: And that's good start game.\u003C/p>\n\u003Cp>Kevin: Good. 404, 404, 404, 404.\u003C/p>\n\u003Cp>Andrew: Well, great. So but here's the thing. If you didn't watch episode 1 so here's the interesting thing. Is technically, technically in Battlesnake, you can actually win if you 404 or have a 500 error every single time because you have to set a default move that your snake makes. And I think that there's something, I mean depending on the implementation, I think there's something cool that you could do about actually having a way to change that default.\u003C/p>\n\u003Cp>Anyways, but then\u003C/p>\n\u003Cp>Kevin: game's still in progress?\u003C/p>\n\u003Cp>Andrew: Yeah. It's been weird and slow. It's, like, not actually still in progress, but the platform's taking a bit of time to catch up with, I think, what's actually going on. So Why is it 404ing?\u003C/p>\n\u003Cp>Kevin: Root to talk. Kevin. Host. So we'll rebuild that, and we'll restart directors. I was using the wrong method right here.\u003C/p>\n\u003Cp>It's a post request to slash move. Okey dokey. So create game, add ready set battlesnake, hit start game. That should be better. There we go.\u003C/p>\n\u003Cp>So move gets hit. Oh, so this is it. So the data comes in. Yes. Req dot, req dot body.\u003C/p>\n\u003Cp>Fantastic. So now let's move all of this over. I might do a little bit of cleaning up as we go.\u003C/p>\n\u003Cp>Andrew: It'd be interesting. Yeah. Never mind. Let's let's get to it.\u003C/p>\n\u003Cp>Kevin: Let's get to it.\u003C/p>\n\u003Cp>Andrew: Go ahead. I was gonna say it's it'll be interesting if we if we wanna add the start and end point and log what's coming through there. I think there's something interesting in that as well, just to kinda see what that looks like before the logic is implemented. Because it does get really busy once the logic is implemented. At least right now, we're just getting those 404 errors or, like, the basic logs.\u003C/p>\n\u003Cp>Kevin: It's a start. And is it a post request? I think it is. Yep. Yep.\u003C/p>\n\u003Cp>Post. We probably should respond. Right?\u003C/p>\n\u003Cp>Andrew: Yeah. And and it'll it'll get angry with us. But\u003C/p>\n\u003Cp>Kevin: And and. Yep. And just because they are gonna get in the way, I'm just gonna pop them down there. Okay. Let me just, cough.\u003C/p>\n\u003Cp>Let me mute.\u003C/p>\n\u003Cp>Andrew: Yeah. And so what's interesting here is so I think we we said this earlier, like, the start and end points. You don't really need them for anything, but they're useful if you wanna implement some more serious logic, which we may do sort of with our direct as Battlesink down the road. So you'll see here once we rebuild and then save again, you'll actually be able to kinda see the stuff that comes in\u003C/p>\n\u003Cp>Kevin: Yeah.\u003C/p>\n\u003Cp>Andrew: Through there.\u003C/p>\n\u003Cp>Kevin: So we need the width The height. And the and the height and the Head. Snakes. I think that's all we need. From rec.body.board.\u003C/p>\n\u003Cp>Right? Yeah. It's all in this thing called board. Yeah. Great.\u003C/p>\n\u003Cp>And the other thing we need is my head, and I am just gonna copy and paste that little convenience there because that's that's wicked.\u003C/p>\n\u003Cp>Andrew: If you by the way, if you really don't wanna have those 2 constant, like, lines that you have there, if you're just, like, really focused on compressing, you can technically pull it from that sneaks variable that you're defining in there. It's just\u003C/p>\n\u003Cp>Kevin: a little bit more more to do it. Absolutely. Yeah. Just can't I mean, because\u003C/p>\n\u003Cp>Andrew: be bothered.\u003C/p>\n\u003Cp>Kevin: Can't be bothered. Right. So so so we've got the we've got the width and the height. So first thing we were doing is don't don't move out of avoid walls, and we're just gonna copy and paste it. Now we're not.\u003C/p>\n\u003Cp>First thing we're gonna do is set do the is moves we're gonna do the is moves safe thing. That's the other bit of, boiler plating, I guess.\u003C/p>\n\u003Cp>Andrew: Yeah. And to be honest with you, you don't I mean, so this is like the the default logic. Technically, you can implement logic that doesn't use this, but this is just like the most straightforward way to do it. So\u003C/p>\n\u003Cp>Kevin: Sure. So this is avoid walls. So my head exists. It's movesafe.leftforce. Great.\u003C/p>\n\u003Cp>Board height. Interesting. I think we just called that\u003C/p>\n\u003Cp>Andrew: We call it height Height. So we just need to change all of those heads to my head and all of those heights to\u003C/p>\n\u003Cp>Kevin: No. Head to head is correct. My head. Oh, you just\u003C/p>\n\u003Cp>Andrew: got to use my head? Okay.\u003C/p>\n\u003Cp>Kevin: Yeah. But board height. So what we're gonna do here is say height. We'll put bring that in as board height so I don't have to rewrite everything. We'll bring this in as board width.\u003C/p>\n\u003Cp>There we go. Job done. I'm also just while we're here, I'm just gonna just gonna we'll make things just a little more little more succinct, perhaps. This is valid. Right?\u003C/p>\n\u003Cp>Just so it's 4 lines as opposed to so many so many lines.\u003C/p>\n\u003Cp>Andrew: This is valid JavaScript. Really? Yeah. I hate indenting so much.\u003C/p>\n\u003Cp>Kevin: I would But indentation doesn't matter. So we avoid we avoid the walls. Great. And then it's avoid bodies. And we've already got the snakes.\u003C/p>\n\u003Cp>We've already got snakes stored here. So that is the equivalent of that line there. And so we're gonna take this for loop. We will dump that in here. And once again, I'm just gonna take this moment to just, to just clean up a little bit.\u003C/p>\n\u003Cp>This time because the conditionals are longer, I probably am inclined to leave them to leave them like this, but just just remove a bunch of, you know, white spacing. And there's some funky indentation going on here. There, boom. There we go. All right.\u003C/p>\n\u003Cp>Then if there's any save moves left, the console log and we return move down. Oh, if if there are none left, we go down. I got it. I got it. I got it.\u003C/p>\n\u003Cp>So, all the safe moves if no go down. Great. There we go. And it won't be return. It'll be red stop return red stop JSON.\u003C/p>\n\u003Cp>And then it won't go further down. Alright. And it will have returned there, so it won't continue. So I don't need to announce or anything. We pick the next move.\u003C/p>\n\u003Cp>I am noticing hang on. Hang on. Game state dot turn is not correct. Let's make sure my head piece. Yep.\u003C/p>\n\u003Cp>Snakes. Everything there is fine. It's just it's just here. Game state doesn't exist. That's req.body.tan, which means oh, no.\u003C/p>\n\u003Cp>Req. Yeah. That's fine. Req.body.tan. And then res.json, then we'll look at safe moves.length, pick the next move.\u003C/p>\n\u003Cp>We'll log it. And we will return it. I'm sorry. It isn't a string. It's a it's an object, isn't it, with a move value?\u003C/p>\n\u003Cp>Andrew: It is\u003C/p>\n\u003Cp>Kevin: Next move. So I think I think that is that's it. That's that's\u003C/p>\n\u003Cp>Andrew: a whole efficient, Kevin. That was wonderful. Okay.\u003C/p>\n\u003Cp>Kevin: Thank you very much. Thank you very much. Yeah. I think that is our our snake as implemented last time. Let's rebuild the extension.\u003C/p>\n\u003Cp>Let's restart the. Once again, that shouldn't need to be the case, but it is just at this point we're recording, so so be it. And oh, that game in progressing has persisted to be a challenge.\u003C/p>\n\u003Cp>Andrew: It has. So ignore. If I refresh,\u003C/p>\n\u003Cp>Kevin: does it know it's done? No. Okay. So New game. We'll have to keep.\u003C/p>\n\u003Cp>New game, and we'll send in, I don't know, loopy bot. Or does loopy bot just do the\u003C/p>\n\u003Cp>Andrew: Just does the loop. Yeah. Yeah. Hungry is good.\u003C/p>\n\u003Cp>Kevin: Hungry is probably a good little test.\u003C/p>\n\u003Cp>Andrew: Hungry is good. He was a good test for us last time. So I feel like we can see how he responds.\u003C/p>\n\u003Cp>Kevin: Okay? Obviously, it's a little more verbose down here. You can reduce the amount of logs with an environment variable. We died that, you know, that's the end state. So, oh, wait.\u003C/p>\n\u003Cp>If it won't, can I hit play? Yeah.\u003C/p>\n\u003Cp>Andrew: You can hit play.\u003C/p>\n\u003Cp>Kevin: And sorry. Show the coordinates. Fast, please. Save game.\u003C/p>\n\u003Cp>Andrew: No. Just go back. Just go\u003C/p>\n\u003Cp>Kevin: back. No. Oh, do I just I'll back back back back back. Alright. So r one is the orange is the, oh, we won.\u003C/p>\n\u003Cp>Andrew: So what did\u003C/p>\n\u003Cp>Kevin: it say\u003C/p>\n\u003Cp>Andrew: in the logs that you thought we lost?\u003C/p>\n\u003Cp>Kevin: No. No. We we didn't lose. It ended. I sorry.\u003C/p>\n\u003Cp>I said we died, but I meant it ended.\u003C/p>\n\u003Cp>Andrew: What does the what does the actual response say though? I'm not I'm curious. I don't have any idea.\u003C/p>\n\u003Cp>Kevin: It is game ID rule set map stand up source. The oh, it's the it's an n the last 10.\u003C/p>\n\u003Cp>Andrew: Yeah. Yeah. But does it say 1?\u003C/p>\n\u003Cp>Kevin: Let's find out.\u003C/p>\n\u003Cp>Andrew: I feel like it somewhere should say 1.\u003C/p>\n\u003Cp>Kevin: I think no. I think snakes is just one long. Oh, yeah. But it could be you or anyone else, I suppose. Fair\u003C/p>\n\u003Cp>Andrew: enough. It may not say it in there. I'm just curious.\u003C/p>\n\u003Cp>Kevin: Maybe it's the fact you still exist. I mean, let's find out when we die. Do you know? Oh, yeah. If we look at you, it has the ID oh, let me, woah.\u003C/p>\n\u003Cp>Andrew: There we go. I was gonna say I feel I felt like I wasn't seeing it, but I\u003C/p>\n\u003Cp>Kevin: feel like Sorry. That was, that was a keyboard shortcut thing going on. Alright. It has a health. I wonder if the health knows that.\u003C/p>\n\u003Cp>I mean, we'll just do it a couple of times, but let's let's watch. Let's watch our our little sneaky there.\u003C/p>\n\u003Cp>Andrew: You do it well. Hey. Like, that logic is good. Okay.\u003C/p>\n\u003Cp>Kevin: Head to head combat, which we haven't built logic for. Maybe we'll do that this time.\u003C/p>\n\u003Cp>Andrew: It's a good idea. I\u003C/p>\n\u003Cp>Kevin: think we because I because I kind of feel like that was what we intended to do this whole episode. Yeah. Congratulations. We're done because we can't build it in flows, and that's the that's the thing here. I have one other I maybe you can execute a flow from Yeah.\u003C/p>\n\u003Cp>From this, and then this just becomes the entry point into the flow.\u003C/p>\n\u003Cp>Andrew: That's what I was just gonna say. I was like, is there a way that we could set up the one of the endpoints in here and then have the trigger in flows? You're able to do everything from there?\u003C/p>\n\u003Cp>Kevin: Maybe.\u003C/p>\n\u003Cp>Andrew: That might be fun. I feel like that's an interesting way because I I do think that flows is like this interesting.\u003C/p>\n\u003Cp>Kevin: I've never done it, but, I'm sure it will be fine. So let let's take another look at some new extensions documentation that was recently written. The resources on the not this one. I'm looking for resources, components, packages, extension services. Here we are.\u003C/p>\n\u003Cp>So underneath directives, every time you make a request, it's actually going off to one of these services, which then, you know, which then executes your your request. And inside of extensions, you can also access these services. Various services are available within directors, including item service, collection service, file service, and they're available within the extensions context. So there is a router here in the handler, but I think we can also pass in this context. Right?\u003C/p>\n\u003Cp>Let me just think about the best way to do this. I think it might be just playing around with this router dot get here. So, so we have this context. I'm gonna bump that back up actually. And if we console dot log, the context and we see just here, we, destructure get schema and services and then we destructure the individual service.\u003C/p>\n\u003Cp>So let's let's just do that outside of the individual. Outside of the individual. No. I think for just a moment, we'll do it just inside of this one just because this is the one we're gonna hit. So we don't we don't just have to mess with that.\u003C/p>\n\u003Cp>And then instead of doing this, let's just console log the the services and just see what happens. I'm actually gonna do a console dot here, services dex null. And what this will do is effectively a console log that never does this kind of cut off thing. It'll always show you all of the nested layers just because I'm not sure what it looks like and I don't wanna I'd rather just be a little more verbose once. So dotcom.\u003C/p>\n\u003Cp>So what we're gonna do here is go back to our root endpoint and hit enter. Oh, sorry. Not root. R snake.\u003C/p>\n\u003Cp>Andrew: There we go.\u003C/p>\n\u003Cp>Kevin: And what happened here? So we have some services. Let's, yeah, let's just expand this for a moment. Webhook service. I, there is a flow.\u003C/p>\n\u003Cp>I, I believe a flow service.\u003C/p>\n\u003Cp>Andrew: Oh, nice.\u003C/p>\n\u003Cp>Kevin: Gonna that's gonna wanna be the one that we I mean, is this the one we wanna I don't know. I mean, all we wanna do is hit all we wanna do is hit an endpoint. But Yeah. Let\u003C/p>\n\u003Cp>Andrew: let's go for have\u003C/p>\n\u003Cp>Kevin: a let's have let's have a play. Sorry. Go on. No.\u003C/p>\n\u003Cp>Andrew: I was just gonna say, do we wanna go in? I don't know. Never mind. Let's let's test this out and then and then we'll go forward from there.\u003C/p>\n\u003Cp>Kevin: Sounds good. So let so what you do here is you create a new service with the collection there. So you know what we'll do here. There are some commonly used services. We can just refer to the full list of services directly in the code base here, which is actually what I should have just done.\u003C/p>\n\u003Cp>So we have flows here. So we can create flows, create many, update, delete. This actually isn't what we want because this is all about running flows. Sorry. And the the crud of flows.\u003C/p>\n\u003Cp>And we don't wanna do that. We just wanna we just wanna run a flow. So maybe the first thing we sorry. Go ahead.\u003C/p>\n\u003Cp>Andrew: No. No. No. Is it just the webhooks that we want, the webhooks service then? No.\u003C/p>\n\u003Cp>Because that's to create a webhook. That's\u003C/p>\n\u003Cp>Kevin: not Yeah. Exactly. That's outbound. So so what we'll do here is we'll go to the admin. Oh, wow.\u003C/p>\n\u003Cp>We will create a little flow and we'll see if we can trigger that from our code. That's basically the test here that it works. So we create a flow. We'll call this info. Snake info.\u003C/p>\n\u003Cp>All of that feels good. Maybe traffic activity in logs. I think that's fine. Then we have the webhook. Webhook.\u003C/p>\n\u003Cp>You're right. It will be a get request, response body, data of last operation.\u003C/p>\n\u003Cp>Andrew: Feed all the data just to have it?\u003C/p>\n\u003Cp>Kevin: That that that's happened. So oh, no. So this is the response body. This is what it will respond with. We only want to we wanna create the response and send it.\u003C/p>\n\u003Cp>But, actually, what you're describing is it was here. It was this option here, track activity in the box.\u003C/p>\n\u003Cp>Andrew: Alright. There we go.\u003C/p>\n\u003Cp>Kevin: So back to the trigger setup, we'll turn cash off. We don't want it to be asynchronous, get request, save, And we will re- we- it will be the data from the last operation. So we'll just run a script and we will just return this, I think. I mean, that makes sense. Yeah.\u003C/p>\n\u003Cp>That works. I've not done this before. So we are we are really finding out together how this works.\u003C/p>\n\u003Cp>Andrew: Works is very smooth. I like just being able to have it.\u003C/p>\n\u003Cp>Kevin: I really like it too. So we've got a save. So now we have this idea of a flow, and I think that's the thing that's interesting to me at least.\u003C/p>\n\u003Cp>Andrew: So you'd rather use the ID than actually just use the flow endpoint, like the webhook endpoint?\u003C/p>\n\u003Cp>Kevin: I could. I don't think that's, like, the proper way of doing it. But could we do that? Yeah.\u003C/p>\n\u003Cp>Andrew: I mean, I feel like I feel like Okay. I'm interested in seeing both ways, right? I'm interested in seeing ID flows, which I think is the proper way, and then just calling the webhook from within the code. Right?\u003C/p>\n\u003Cp>Kevin: Yeah. I'm gonna be I I don't even know how to make a web request in these off the top of my head.\u003C/p>\n\u003Cp>Andrew: Just I think it's just Axios, isn't it?\u003C/p>\n\u003Cp>Kevin: I think it's just Axios. But but how is Axios, like, exposed? And do you wanna know what? I'm pretty confident there are just some guides that were written around this that I think will just help us. Endpoints create a public API proxy.\u003C/p>\n\u003Cp>Here we go. This will this will help. Yeah. It's a it's a fetch.\u003C/p>\n\u003Cp>Andrew: Oh, alright.\u003C/p>\n\u003Cp>Kevin: Cool. So we will so we hit slash. So what we're gonna do here, we're we're forget all these services for a minute. They don't I don't think they're gonna matter.\u003C/p>\n\u003Cp>Andrew: No. They won't matter because literally we're just basically now that we've created that flow, we're just doing a call to that webhook and then\u003C/p>\n\u003Cp>Kevin: Yeah. We're gonna do a response, await, fetch, and then slash flows, async. Okay. And then, yes, this is response okay, response Jason await Jason, all of this jazz. I normally, honestly, just do this pretty low rent, then r r dot JSON and our response is the data.\u003C/p>\n\u003Cp>So I think we can just return reds dot JSON response. Yeah? Yep. I might just be explicit here. Flow response.\u003C/p>\n\u003Cp>Flow response. Yeah.\u003C/p>\n\u003Cp>Andrew: Yeah.\u003C/p>\n\u003Cp>Kevin: And maybe yeah. That yeah. Screw it. Let's try it.\u003C/p>\n\u003Cp>Andrew: Let's do it. Gotta get this I feel like this is why I enjoy, like, non typed languages because you just mess around and see what happens.\u003C/p>\n\u003Cp>Kevin: Yeah. I mean, you can build extensions just as an FYI with TypeScript. I just didn't oh, yeah. I thought this might be a little easier. So if I hit refresh here, we should see the same the same thing the same thing.\u003C/p>\n\u003Cp>Oh, wait. Hang on. I don't know why that's happening. Oh, was there an error? You have triggered an unhandled rejection.\u003C/p>\n\u003Cp>Failed to pass URL. So it didn't like the\u003C/p>\n\u003Cp>Andrew: Yes. So just add\u003C/p>\n\u003Cp>Kevin: the link to the I mean, sure. But that that doesn't make this portable. Right? That isn't let me just have a quick think here.\u003C/p>\n\u003Cp>Andrew: Oh, I see what you mean. You wanna be able to pat yes. Okay. I see what you mean.\u003C/p>\n\u003Cp>Kevin: Because then if we deploy it somewhere else, this will Yeah. This will still work. That's that's that's gonna have another little look. Let's keep having a little look here. So still sort here.\u003C/p>\n\u003Cp>No. Because now I'm right because this is the flows. All I wanna do is know, yeah, how do I run a flow? Let's just do you wanna know I think I've just had I think I've just had an idea on how this works,\u003C/p>\n\u003Cp>Andrew: which is\u003C/p>\n\u003Cp>Kevin: actually not looking at the services. It's looking at the the way the SDK does it. So if we look at the, extensions, packages, sure. The SDK just like type, type doc. This is just the the full dump of everything the SDK can do.\u003C/p>\n\u003Cp>There is an endpoint in here called something there's something in here for running flows. Andrew, I'm going It is I love a c.\u003C/p>\n\u003Cp>Andrew: So even if you write the documentation, everybody, you still need to read the documentation.\u003C/p>\n\u003Cp>Kevin: You do still need to run it. Here it is. Right. Create a flow, retrieve a flow, list flow. So this is all the stuff we just saw, but Yes.\u003C/p>\n\u003Cp>Here we are flow with get webhook trigger, trigger flow. What is going on here? Let's take a little look. Failed to pass URL from /flow/trigger/uid.\u003C/p>\n\u003Cp>Andrew: Yeah. That I mean, that makes sense to me because it's not a full URL. Right?\u003C/p>\n\u003Cp>Kevin: Do you wanna know what I did sneakily on on the side here. I have a chat with one of my colleagues because, of course, I did because I have that power. It does just need to be the full URL. So let's stop. Let let's stop battling against I was right.\u003C/p>\n\u003Cp>You're right.\u003C/p>\n\u003Cp>Andrew: You're right. But you're right. It's not portable. You're very true. It's like you'd now need to, like, worry about changing this if you change where you're you're hosting it.\u003C/p>\n\u003Cp>Kevin: So I do think Which I think won't matter for the scope of this. So now built that, restarted that. So now if we hit this, it did return and it did it by getting by by getting the the, yeah. So, oh, that's really exciting. Okay.\u003C/p>\n\u003Cp>Let's abstract this logic out then. Let's get this whole move thing. Let's just comment it out. We know it works. Okay.\u003C/p>\n\u003Cp>And what we're gonna do here instead is we have a post. And so, let's set up a new flow. Not here. Let's set up a new flow. Oh, that's so exciting.\u003C/p>\n\u003Cp>Okay. Cool. Snake.\u003C/p>\n\u003Cp>Andrew: So we're gonna we're gonna create a move we're gonna create literally recreate this entire logic as separate flows. Why not do it as one flow, though?\u003C/p>\n\u003Cp>Kevin: Oh, because it No. No. No. Just because it won't be each endpoint. So everything move is gonna happen in one flow, and that that's yeah.\u003C/p>\n\u003Cp>Yeah. It's cool, isn't it? I think track activity and logs, I'm going to level with you. I'm not I'm going to say don't track anything because it's just gonna it's gonna absolutely explode the logs because it can be every like, the full body every time. And we have the the the logs over here.\u003C/p>\n\u003Cp>So we won't do that. That will be a webhook. Data of lost operation, that's fine. Not asynchronous. Don't cache.\u003C/p>\n\u003Cp>Post. Okay. So exciting. Okay. And we will, run a script and we will return move left.\u003C/p>\n\u003Cp>Right. We'll just we'll just do that to start and just test test it works. Return move. Hit save. And we have an ID for this one.\u003C/p>\n\u003Cp>Great. And, what we're gonna do here? We're gonna do we're gonna copy this. We are gonna change the ID and we're just gonna go post, sorry, methods. I wanna say it's post and I think it's body equals JSON dot stringifyreq.body.\u003C/p>\n\u003Cp>I think that's I think that's the fetch. I think that's the fetch way of doing it. So What\u003C/p>\n\u003Cp>Andrew: is the fetch way of doing it?\u003C/p>\n\u003Cp>Kevin: Is it good? Okay. Fetch. My memory has not failed me today. I'm literally I'm so pumped that we're able to do it in flows.\u003C/p>\n\u003Cp>That was a really good I I think that was yours. You can have that one.\u003C/p>\n\u003Cp>Andrew: No. We're gonna I'm gonna give that that's that was a 5050. We left brain, right brain thing here. I feel like it was one of those days.\u003C/p>\n\u003Cp>Kevin: Okay. Okay. Alright. So we're ready set. Ready set Battlesnake, Hungry Bot.\u003C/p>\n\u003Cp>Sure. And start game. What's happening?\u003C/p>\n\u003Cp>Andrew: It's happening. It's happening. It's happening. It's happening.\u003C/p>\n\u003Cp>Kevin: But it will just go left, left, left, left, and die. Yep. Right? That's fine. Yeah.\u003C/p>\n\u003Cp>That's fine. It's still\u003C/p>\n\u003Cp>Andrew: it's still going into\u003C/p>\n\u003Cp>Kevin: the world. Awesome. Which means we are done with the code. We're not done. We'll need it for reference.\u003C/p>\n\u003Cp>Yeah. But we are in flows now. It's happening.\u003C/p>\n\u003Cp>Andrew: What's interesting here too, folks, is, like, you like, literally with those two endpoints, like, you don't actually need to implement start and end. Like, you can stop with just those 2 if you're like, I don't wanna have to manage 4 flows. Like, you don't actually need start and end at this point. And so I think, like, don't use them.\u003C/p>\n\u003Cp>Kevin: So first thing we did was avoid walls. I'm gonna run a script here. We'll return it, and then I'm just gonna do the it's annoying. You have to kind of create it from an existing path. Yeah.\u003C/p>\n\u003Cp>And then we're gonna take that and dump it in there. Okay. Avoid walls. Back to the back to the primitives. Now, you know what we didn't do?\u003C/p>\n\u003Cp>We didn't because we don't have the logs. We actually don't quite know what it looks like in here, which is annoying. Just a thought I've had.\u003C/p>\n\u003Cp>Andrew: Yeah. It's true.\u003C/p>\n\u003Cp>Kevin: But I think I know.\u003C/p>\n\u003Cp>Andrew: Well, I mean, let's see. Let's see what happens here. And I think the cool thing here is, like, now we return to, like, traditional programming as well. Right? There's a lot of different ways you can implement this within the flow.\u003C/p>\n\u003Cp>Like, there's so many different operations. Technically, you could feedback a lot of the information and then make, like, decisions there or just do it.\u003C/p>\n\u003Cp>Kevin: Let's let's return the data. Like, that's the whole data chain. And then so this first time, this is gonna fail absolutely miserably. But, oh, no. It won't log anywhere, will it?\u003C/p>\n\u003Cp>Do we\u003C/p>\n\u003Cp>Andrew: wanna turn on those logs then? I know we didn't want\u003C/p>\n\u003Cp>Kevin: to, but Yeah. Yeah. Can I turn them on then turn them off? Yes. Fine.\u003C/p>\n\u003Cp>Yeah. That's fine. Yeah. Yeah. Yeah.\u003C/p>\n\u003Cp>Sorry. I thought it was a one and done kinda deal. So let's create a new game. Create rematch. Okay.\u003C/p>\n\u003Cp>Cool. Whatever. Sure. Refresh. And there should be many Yeah.\u003C/p>\n\u003Cp>8. That was 8 moves. So it's it's gonna get unwieldy, but this is what matters.\u003C/p>\n\u003Cp>Andrew: That's great. That's awesome. Yeah. So we get\u003C/p>\n\u003Cp>Kevin: Except the distinct so the trigger where's where's all the, not being oh, so Are we not sending it? Are we not sending it precisely? And this is why I wanted to check. Yeah. Yeah.\u003C/p>\n\u003Cp>Oh, wait, what\u003C/p>\n\u003Cp>Andrew: did that say failed up there? Go back up to the options.\u003C/p>\n\u003Cp>Kevin: No, no, no, no, no. Force. Okay. Cache enabled force, cache enabled force. So cool.\u003C/p>\n\u003Cp>We have this fetch. We send that is how you that is how you do it. Right?\u003C/p>\n\u003Cp>Andrew: I mean, I could have been wrong. I was assuming, but ah, here we go. I'm glad that it has happened.\u003C/p>\n\u003Cp>Kevin: Yeah. Body.stringify. Body data type must match content type header, but I I don't think sure. Do you wanna layer what? Sure.\u003C/p>\n\u003Cp>Let's just whack that header in. Just in case it's that.\u003C/p>\n\u003Cp>Andrew: Yeah. See what happens.\u003C/p>\n\u003Cp>Kevin: I'm not losing any sleep over over adding that. Is that an error? Oh, no. We've left a log we've left a login somewhere. All the serve hang on.\u003C/p>\n\u003Cp>They're all the services.\u003C/p>\n\u003Cp>Andrew: Yeah. Didn't we didn't we remove that?\u003C/p>\n\u003Cp>Kevin: No. It's the it's the context. It's this console log context.\u003C/p>\n\u003Cp>Andrew: Oh, there we go.\u003C/p>\n\u003Cp>Kevin: I was like, that's a big that's a big object. So right. Restarting. Okay. Okay.\u003C/p>\n\u003Cp>Okay. We're gonna create rematch.\u003C/p>\n\u003Cp>Andrew: Let's get some logs.\u003C/p>\n\u003Cp>Kevin: Nice. Oh, head to head. Some quick a nice quick death. Okay. Are we getting Payload.\u003C/p>\n\u003Cp>There we go. There we go. There we go. So it's in trigger dot. Body.\u003C/p>\n\u003Cp>Body, I think.\u003C/p>\n\u003Cp>Andrew: Yep. Yeah. Trigger dot body. Yeah.\u003C/p>\n\u003Cp>Kevin: That looks correct. So, I mean, there's one way to find out, right, which is, so, trigger data dot trigger dot Bobby. Nice one. Bobby. So let's go and take this first up.\u003C/p>\n\u003Cp>So first thing we'll do, of course, sorry, is we will is move safe. We have to think about how we, because we, we need to kind of alter this value as we go. Maybe we return that. Maybe we return that with each step. Right?\u003C/p>\n\u003Cp>So we'll start with a is move safe and we'll just return it every time. So Okay. We'll start this time, we'll create it. I also think that could be a const. Anyway, avoid walls.\u003C/p>\n\u003Cp>So we need these values. Do you\u003C/p>\n\u003Cp>Andrew: wanna bump up the text size there a little bit more? Thank you.\u003C/p>\n\u003Cp>Kevin: You know what? I'm actually inclined. Let's rename this one. Let's call this one set up variables. Yeah.\u003C/p>\n\u003Cp>Like that. Let's call them let's call it globals. We'll just make the key global so it's a little shorter. All we're gonna do is return is return. We're gonna return what was it called?\u003C/p>\n\u003Cp>Is move safe. We will also do forward width. It's going to be data.trigger.body.width. Old height. What was the other thing?\u003C/p>\n\u003Cp>Oh, my head. So that's all of that. And then, we yeah. Just return that. Right?\u003C/p>\n\u003Cp>Yep. Sponsored global. Yeah. Yeah. We will.\u003C/p>\n\u003Cp>Just just I I wanna be I wanna be testing early in Austin even though, you know, we're not doing much there. I just wanna I just wanna be validating that we haven't broken something there. So we will create a rematch. So all we should be getting now is an extra property in the data chain. Wow.\u003C/p>\n\u003Cp>That was a really short, really short game. I think it was, like, 2 moves. Yeah. Oh, rec is not defined. I think I left in a a remnant of what it was before.\u003C/p>\n\u003Cp>Rec.body. Yeah. Data. Trigger dot body. That's why we test.\u003C/p>\n\u003Cp>Create a rematch. What else have you used Flows for in the past?\u003C/p>\n\u003Cp>Andrew: So I did the, the DevCycle Directus integration that I built out. So this was, it's worth checking out, just to see, like, the different use cases here. So it basically allowed for you to do an API call to a patch API on the dev cycle, patch API endpoint on the dev cycle platform to, like, pass basic data into into the service and to, like, put, like, an edge database for feature flag management. We might use that actually, when we start to do the dev cycle integration just because it's a really fast way, and I know that it already works with Directus. So, nice.\u003C/p>\n\u003Cp>But yeah.\u003C/p>\n\u003Cp>Kevin: That's called EdgeDB. EdgeDB. Yeah.\u003C/p>\n\u003Cp>Andrew: So it's just, it's just, like, a faster way to pass data. So how are we doing here?\u003C/p>\n\u003Cp>Kevin: No. No. But if I refresh, I think I fixed it.\u003C/p>\n\u003Cp>Andrew: Yeah. Right.\u003C/p>\n\u003Cp>Kevin: That's everything in the in the global. Now I'm not sure. So I think is move safe is one that we might need to pass, like every move we're going to need to we're going to need to reset that, but that's fine. So cool. Next thing we'll do then is avoid walls.\u003C/p>\n\u003Cp>Avoid walls. We're gonna run a script. We have data, and we have our avoid walls logic here.\u003C/p>\n\u003Cp>Andrew: Yeah. I feel like this is also just a really nice visual way to represent this whole\u003C/p>\n\u003Cp>Kevin: This those are the steps.\u003C/p>\n\u003Cp>Andrew: I really wanna go and find my Node JS snake or my, my Node RED snake that I created because I did the same it was the same process that I went through.\u003C/p>\n\u003Cp>Kevin: Was it sorry. Just very quickly. Well, did I call it my head still? I think I did. Yeah?\u003C/p>\n\u003Cp>Yep. You did. Yep. So the nice thing about this is we have is say is move safe left and all of this. So I think we wanna create a new variable called is move safe, and the value of that, I think this works, is gonna be data dot globals.\u003C/p>\n\u003Cp>Was it multiple? No. Global.\u003C/p>\n\u003Cp>Andrew: Yeah. Global.\u003C/p>\n\u003Cp>Kevin: Yeah. I think so. Yeah. Dot is move safe. Right?\u003C/p>\n\u003Cp>Andrew: Yeah. This this\u003C/p>\n\u003Cp>Kevin: is And then yeah. This is\u003C/p>\n\u003Cp>Andrew: something that's really interesting to know having used flows before. I was not taking advantage of the globals like that or not the globals because that's the thing you're creating here, but, like, the But the data chain. Touch on data chain. I thought you lost the data, basically No. As you went through.\u003C/p>\n\u003Cp>So that's interesting.\u003C/p>\n\u003Cp>Kevin: And I've had another thought about this. I think the way we're gonna do this is using last. So last, dollar sign last is a dynamic variable that will always be the output of the last step. But that means this line will become basically a boilerplate first line on every step in, I think. I think this creates a copy.\u003C/p>\n\u003Cp>We'll find out. We'll find out together. So next thing we're gonna do is we're going to destructure, my head board width, and board height from data dot globals. And then the rest of the code should be able to be untouched. Right?\u003C/p>\n\u003Cp>Exactly. Yeah.\u003C/p>\n\u003Cp>Andrew: It looks good. The\u003C/p>\n\u003Cp>Kevin: only and the only thing we wanna the only thing we wanna return is is move safe. Only thing we care about. Yeah. If this works, I'm gonna be so pleased with myself. I have a hunch it won't just because, of course, why would it?\u003C/p>\n\u003Cp>Let me just move return move out the way. In fact, we can remove that. So no. Of course not. Sorry.\u003C/p>\n\u003Cp>Ah, I've deleted it now. It's fine. We're here. So return move because what we're gonna do in this step is we're gonna just do that last bit of logic. If all there safe moves, if no go down.\u003C/p>\n\u003Cp>So we're gonna get rid of that. All we care about is next move and return. Yep. Because that is a a step between where we are and where and where we're going. So I have in my here we are.\u003C/p>\n\u003Cp>This is what we just had in our last step. So we have beta dot globals. Now is move safe. There we go. So next move, safe moves.\u003C/p>\n\u003Cp>Is it safe safe? Why is it called safe moves? Oh, here. I need this line too. I'm gonna call that one.\u003C/p>\n\u003Cp>Safe moves. Object dot keys is move safe. Filter down is move safe key. Fine. Fine.\u003C/p>\n\u003Cp>And that's just getting the the the values. Then next move, is safe moves, math floor safe moves. So that that should be it, And then we should just be able to return move next move like so. So that's the last step in. That's the last step in the chain.\u003C/p>\n\u003Cp>Let me bump that up one more time. Sorry. So that's all the logic again. We have that boilerplate starts. We don't quite know if that works yet, but in theory\u003C/p>\n\u003Cp>Andrew: It should.\u003C/p>\n\u003Cp>Kevin: Theory, it should. It should. Dangerous last words. Those. Sorry.\u003C/p>\n\u003Cp>So we'll just set ready set battlesnake in there because we're not equipped to have multisnake yet.\u003C/p>\n\u003Cp>Andrew: Nope. Did not work. What are our logs saying?\u003C/p>\n\u003Cp>Kevin: Thank you. Thank you very much. Yes. It did not work.\u003C/p>\n\u003Cp>Andrew: You're welcome. I feel like I need to add these\u003C/p>\n\u003Cp>Kevin: useful Last is not defined. Absolutely. I know exactly where the typo was. It's data dot last, and it will be wrong there as well. Data dot last.\u003C/p>\n\u003Cp>Right? Yep. Yeah. Cool. And what's nice now is you're not having to restart Directus every time because now it's handled by the flow.\u003C/p>\n\u003Cp>That's wonderful. It is cool. There was a little bit of of, let's call it friction first time. Okay. That's not ideal.\u003C/p>\n\u003Cp>42, 44, payload. Cannot convert undefined or null type. Okay. What are we? Payload after after but here's the thing.\u003C/p>\n\u003Cp>After avoid walls, look at that. It did say up force, which means everything's working until the return move. So that's fine. So that means it works, this whole passing passing is move safe each step. Yeah.\u003C/p>\n\u003Cp>But where it's breaking is this last one.\u003C/p>\n\u003Cp>Andrew: Okay. So let's look at this again. So async function\u003C/p>\n\u003Cp>Kevin: the error was cannot let's actually read the error. The error says, cannot confer undefined or null to object, which means the issue is here in object dot keys. So we have is move move safe. I think it can just be data dot last if that's all we're passing step to step because we don't return an object with is move safe. We just return is move safe, which means here again, although\u003C/p>\n\u003Cp>Andrew: I mean, let's see. Let's see what happens.\u003C/p>\n\u003Cp>Kevin: Yeah. I think I think that'll fix it. So if we hit create rematch Yes. It avoided pause. Nice.\u003C/p>\n\u003Cp>Lovely stuff. And let's just, let's just validate that was indeed what we needed it to be. Left.\u003C/p>\n\u003Cp>Andrew: Hey. There we go.\u003C/p>\n\u003Cp>Kevin: Heck, yeah. Which means we're onto we're we're now onto a winner, basically. It's this is the starting logic for our for the no. It isn't. Oh, it's it's somewhere in between that we haven't quite got the standard because this is the first one after the global and this is the last one.\u003C/p>\n\u003Cp>So that's fine. We need to create one more here. Yep. And, what's next? Avoid bodies.\u003C/p>\n\u003Cp>Andrew: Avoid others. Yep.\u003C/p>\n\u003Cp>Kevin: Run script. Paste that in. And we don't need is move safe anymore. We have the data dot globals. And where are the where are where is the logic?\u003C/p>\n\u003Cp>It's here. And just because I think I think it's gonna be easier, I'm just gonna great. Great. And I don't think we need board width or board height anymore. We just need my head.\u003C/p>\n\u003Cp>We have snakes. So I believe we had bored. I think we got bored out. Yes. I'm not sure we did.\u003C/p>\n\u003Cp>Let's look at the globals again. The actual board itself needs to come with 2. So, let's call it snakes. Let's just add snakes here. So data dot trigger dot body dot board dot snakes.\u003C/p>\n\u003Cp>Then let's create this last one again, as avoid bodies and that should have saved, but it's fine. Okay. So we have my head and we have snakes. I think I gen oh, yeah. I genuinely think nothing needs touching here.\u003C/p>\n\u003Cp>Andrew: No, I think it's good. And I think we've the, the code works and we're even past.\u003C/p>\n\u003Cp>Kevin: Yeah. I don't see any point of weakness in this at the moment. So we hit say, oh no, we don't, we just,\u003C/p>\n\u003Cp>Andrew: Famous last words.\u003C/p>\n\u003Cp>Kevin: We do that. Then we do that. So I'm gonna just Very good. We'll hit save. Oh,\u003C/p>\n\u003Cp>Andrew: what's going on? Value fulfilled resolving collection, direct us operations.\u003C/p>\n\u003Cp>Kevin: Value fulfilled resolve. In fact, it has to be I, don't know what that is. Record not unique.\u003C/p>\n\u003Cp>Andrew: For field, resolve and click direct operations. Maybe if we turn off that logging now. Just turn off the logging because I think that's Yeah.\u003C/p>\n\u003Cp>Kevin: But that isn't logging. That's a database save error, but I don't know why. Let let's just forget this was here for a moment like it was before, and it was fine. Okay. That does save.\u003C/p>\n\u003Cp>So if I take this instead and send it to avoid bodies and hit save, it's fine. And then if I go here to return move, fine. Do you wanna know why I'm just gonna walk away slowly?\u003C/p>\n\u003Cp>Andrew: That's the right that's the right answer. If the thing starts working\u003C/p>\n\u003Cp>Kevin: So we don't wanna create a rematch now because we wanna create a game with another snake. Yeah. Yeah. Star hungry\u003C/p>\n\u003Cp>Andrew: in there. Alright. Let's see how this works. Okay. We got it.\u003C/p>\n\u003Cp>He Hang on.\u003C/p>\n\u003Cp>Kevin: We've not had to Yes. Hey. There we go. Yes. Nice.\u003C/p>\n\u003Cp>Get in. Okay. Done. Nice. So the final thing here was this, if there's no safe moves, go down.\u003C/p>\n\u003Cp>Andrew: I don't think we need that. I genuinely it's like a Oh, yeah. Backup. It'll always Yeah.\u003C/p>\n\u003Cp>Kevin: Well, because if there are no safe moves, you're toast anyway.\u003C/p>\n\u003Cp>Andrew: Exactly. So it's really just, like, if you want to have, it I don't know. It's good programming practice for you to build it in, but, like, I think here, like, we can worry about that down the\u003C/p>\n\u003Cp>Kevin: road. Do you wanna know what? Given that this was basically the scope for this episode, I'm gonna add it. Why don't we add 1 one new block in?\u003C/p>\n\u003Cp>Andrew: Right.\u003C/p>\n\u003Cp>Kevin: Because I wanna do avoid avoid head to head.\u003C/p>\n\u003Cp>Andrew: K. Yep. That seems easy. Yeah. Because we're just basically doing look ahead here.\u003C/p>\n\u003Cp>Right? We're looking at, like, one extra block ahead. So so yeah. I mean, so this is the if you didn't catch it on the last stream or on the last, the last episode, we so head to heads, you can do and you can win, but it's all about size and rules, but, like, these head to heads here. But with the logic we currently have, we're only ever looking at what's directly next to us.\u003C/p>\n\u003Cp>So ensuring that, like, if we have another snake beside us or directly in front of us, we'll never go that direction. But if there's a snake that's 2 away in the same direction, so basically, we're repeating the logic that's in here and just or we could technically revise this logic. I think it's similar because it's just like Yeah. Yeah.\u003C/p>\n\u003Cp>Kevin: It will be similar, but I don't I do not think it's the same. So it's not the same. If we ditch this for a moment, we'll leave it as we could see it. So we care about the snakes, every snake, and we're gonna get rid of us. Now there is a a fancy way of doing this, which is, js return array without first item.\u003C/p>\n\u003Cp>To remove the first element in the in the array, you can use shift. Shift removes the first element of the array and returns it. So I think we can do this just to get rid of us first.\u003C/p>\n\u003Cp>Andrew: Yeah. And then we have to find our\u003C/p>\n\u003Cp>Kevin: because then we would have no valid no valid way to go. So So now we care about every other snake. So I'm gonna say head, it's gonna be snake dot body. Does snake have a head too? Does every snake have a head?\u003C/p>\n\u003Cp>Andrew: Everything has a head.\u003C/p>\n\u003Cp>Kevin: Snake dot head. So okay. Cool. And what we wanna do is look one space left, right, up, or down and remove if that's gonna be the direction we go in. Right?\u003C/p>\n\u003Cp>But we I'm just trying to have a think here because I think these are actually much more complex if statements.\u003C/p>\n\u003Cp>Andrew: They are. It's basically if I remember having done this before, it's basically all of those if statements that we had there repeated 4 times, if that makes sense.\u003C/p>\n\u003Cp>Kevin: Which we absolutely will not do. We will we will do, like, a nested loop. But I'm just trying to think through that. I'm just trying to think through, like, what is the logic here? Well, the first thing we wanna do is check every feasible space we can move into.\u003C/p>\n\u003Cp>That's the way we're gonna do it. So feasible directions. I remember encoding Valdez to do stuff like that, and then we had to remember what it meant. Possible next steps. And we actually don't wanna do this inside of this loop of every snake.\u003C/p>\n\u003Cp>We wanna do this just for us. So possible next steps are, is I don't know that because that's gonna be possible next steps is an array is an array. Cool. And we're gonna push into this array everything up, down, left, or right of us. Yeah.\u003C/p>\n\u003Cp>Then we're gonna remove our body piece, our neck. We're gonna remove our neck. Yeah. Okay. Cause we don't want our neck in there.\u003C/p>\n\u003Cp>So that, that will release 3 directions. And then for every snake, we're gonna repeat that logic. We're basically gonna say if any of these match, you remove it. So when we store them in here, we also have to store whether it's up, down, left, or right.\u003C/p>\n\u003Cp>Andrew: K. Alright. Let's see. You have a plan, and I'm very curious to see how this\u003C/p>\n\u003Cp>Kevin: will be implemented. First thing we wanna do basically is say up, down, left, or right. What is the coordinate? So what are the coordinates of that? So we're gonna say, in fact, I I think this might be easier.\u003C/p>\n\u003Cp>So we're gonna go possible next steps dot push. We'll we'll start with left. No. Left is going to be x is, come on, Kevin, you can do this. X is going to be our current X position.\u003C/p>\n\u003Cp>So this is going to be I don't just want my head. I want I want you. So I'm gonna save this. We'll come back here. I don't think we have you.\u003C/p>\n\u003Cp>No. No. We have my hand, which is great. Let's just throw you in there. Then we'll come to avoid head to head and what just while I'm here.\u003C/p>\n\u003Cp>And return move. Right. Return head to head. So now we have access to you in here. So we want Datastock Global's my head, you, and my head.\u003C/p>\n\u003Cp>Cool. So that's gonna be you dot, why why did I do you? We just care about my head. What are you doing, Kevin?\u003C/p>\n\u003Cp>Andrew: Yo. Because you wanted to remove the neck. You were this\u003C/p>\n\u003Cp>Kevin: Yeah. But not oh, yes. I suppose that will be that will become important, but the neck comes later. Yes. So we just wanna say my head dot x minus 1 and y equals my head dot y y.\u003C/p>\n\u003Cp>That's left. Bright. So that's the the same. Up. And so that's gonna be x remains the same, but y this time, r is minus 1 and down, plus 1.\u003C/p>\n\u003Cp>No. It starts the other way around. Down because bottom left is 0. Up. So that's pushing in push in all I mean, it's it's pretty obvious actually by the, by by the name of what's going on.\u003C/p>\n\u003Cp>Then we wanna remove our neck from that. Then there'll only be 3 items in the array. So we'll go possible next steps. How do I do this? Equals poss that becomes a let.\u003C/p>\n\u003Cp>Possible next step, stop filter. Possible next steps. So steps, where the step is not equal to you. You dot what is the it's a bit disruptive, but I'm gonna just what is the what's it called? U.body.body.\u003C/p>\n\u003Cp>Andrew: I love that my my many, many if statements, my heuristic was not okay, but there's this will be so much cleaner though when it's done. I will agree with you there.\u003C/p>\n\u003Cp>Kevin: Thank you. So this is remove net. I think that's what that does. So every item where that because all the body parts are is this. It's an x and y.\u003C/p>\n\u003Cp>Right?\u003C/p>\n\u003Cp>Andrew: Yep. Yep.\u003C/p>\n\u003Cp>Kevin: I I'm a little bit I think I think object, object comparison is a little bit hunky. So what I'm gonna do just to be on the safe side here is say, where s dot where s dot x is not equal to x and where it's not equal to y. So if both those things, no or no and hang on. This is this is important. I'm gonna do it the other way around.\u003C/p>\n\u003Cp>So where they are equal to x and they are equal to y, that means, hey, this item matches or wrap the whole thing in another bracket and we will negate it. Okay. That should be removing. Does that make sense?\u003C/p>\n\u003Cp>Andrew: I'm so glad we have a video because that's super unreadable, but I get the logic makes sense. No. No. No. Not not unreadable.\u003C/p>\n\u003Cp>Unreadable. I mean, like, I feel like if somebody looked at this, they would be like, what is happening in this line?\u003C/p>\n\u003Cp>Kevin: So, I mean, I mean, we're removing the yeah. Yeah. Right. Okay. Sure.\u003C/p>\n\u003Cp>Yeah. So Who needs to\u003C/p>\n\u003Cp>Andrew: read this code except for us?\u003C/p>\n\u003Cp>Kevin: Exactly. No. This makes sense. Okay.\u003C/p>\n\u003Cp>Andrew: Yeah. I\u003C/p>\n\u003Cp>Kevin: think that works. So we removed the neck. So that's removing that's saying all of our possible neck positions. Now we wanna basically figure out all of the every other snake's possible next steps. And if there's any matching item oh, there's a there's a silly, silly little thing going on here, which is we're gonna push in.\u003C/p>\n\u003Cp>I'm gonna just call the variable c to mean coordinates and that and this is where it all starts falling apart in terms of, like, clarity. And I am gonna I am I I've decided I am gonna be more explicit here.\u003C/p>\n\u003Cp>Andrew: No. I think it was\u003C/p>\n\u003Cp>Kevin: I mean, I\u003C/p>\n\u003Cp>Andrew: think it's I think it's very clean code before, but, yeah, I think that this makes a little bit more sense.\u003C/p>\n\u003Cp>Kevin: Yeah. So we have the cohorts. And the reason this matters, It's because we don't just want to store the coordinates. We also wanna store the actual reference to which direction it is. So as well as the quotes, we are gonna say direction left, and we can get rid of that.\u003C/p>\n\u003Cp>We can get rid of that. So direction left, direction right, direction down and direction up. Right? So now we just have to change this bit here because it's not s dot x anymore. It's s dot quartz x and that's dot quartz y.\u003C/p>\n\u003Cp>But it will when it removes it, will physically the directions will no longer and then at the end, we're gonna just return the those directions. So I think that that's an important thing. So we can actually ultimately at the end change is move safe and know what values we are we are removing. Yeah. Because before, there's no how do we know which one's which other than the order we initially push them in?\u003C/p>\n\u003Cp>Andrew: Mhmm. Not a\u003C/p>\n\u003Cp>Kevin: nice. The next thing we want to do is go around every snake, and we wanna basically do this same game here where we go left, right, up, down, push it into an array, cross reference them. If any of them are the same, we know the direction in which they match. Oh, that That's funny. Off reactions gone.\u003C/p>\n\u003Cp>Are you familiar with these?\u003C/p>\n\u003Cp>Andrew: I am, but you're in Arc. Right? Is that where\u003C/p>\n\u003Cp>Kevin: it actually is?\u003C/p>\n\u003Cp>Andrew: Or is it within Riverside?\u003C/p>\n\u003Cp>Kevin: No. It's it's macOS.\u003C/p>\n\u003Cp>Andrew: Oh, it's in Mac. Really? No way.\u003C/p>\n\u003Cp>Kevin: Yeah. But I don't Well,\u003C/p>\n\u003Cp>Andrew: I'm not I'm on Windows. Windows is like, no. We don't we don't think these are things that we need.\u003C/p>\n\u003Cp>Kevin: That is so funny. So now we now we wanna do the same thing for this snake. And this is this is the important thing is that for every snake, we are literally gonna do this exact same logic, except we're not gonna remove the neck. We're just gonna say, oh,\u003C/p>\n\u003Cp>Andrew: are we gonna remove the neck? No. No. We'll just remove the head.\u003C/p>\n\u003Cp>Kevin: No. No. No. No. Because if we you know, because if we're colliding into the neck, we're colliding like that would have been handed by the dome.\u003C/p>\n\u003Cp>Yep. Yeah.\u003C/p>\n\u003Cp>Andrew: Exactly. Yeah. So it's just the head. Exactly. Exactly.\u003C/p>\n\u003Cp>Kevin: So we just care about this logic. So possible next steps. And I am just gonna just shrink this down slightly because it'll be contextually clear what's going on here. So it's no longer my head. It's no longer my head.\u003C/p>\n\u003Cp>It's head. Head. Okay. And so that's so that's that. We're doing that for every single snake.\u003C/p>\n\u003Cp>And now this is the important thing we are going to compare. Yeah. We are going to compare. Let's just have a think about this. We actually don't care about the directions this time.\u003C/p>\n\u003Cp>The directions are irrelevant. I think, I think we'll leave them in for the sake of having the same data structure. So we know exactly what we're doing, but this bit's irrelevant. We just yeah. It's it's irrelevant.\u003C/p>\n\u003Cp>So this n is going to go before it's just one big array of every snake. We don't need to have it on a per per snake basis. So this is us populating and with every viable direction that could ever be in. Actually, I've had a change of heart. We are we are gonna Sure.\u003C/p>\n\u003Cp>That down.\u003C/p>\n\u003Cp>Andrew: And then we're just gonna be comparing our end versus our possible next steps. Hence, look, we're\u003C/p>\n\u003Cp>Kevin: just gonna be You absolutely you absolutely got it.\u003C/p>\n\u003Cp>Andrew: I like it. Okay. This is cleaner than I thought. I was definitely you were, I get where your head is at with removing some of this stuff now. I love it.\u003C/p>\n\u003Cp>It took it took you getting to the end of this for me to be like, yeah. I I this makes sense. Good. Good. Good.\u003C/p>\n\u003Cp>Thank you very much. Thank you, Ro. Smart code. Very much.\u003C/p>\n\u003Cp>Kevin: So now what we're gonna do is compare every single value in n. So we're so what we're gonna do now is and yeah. Yeah. Perfect. So we'll we'll loop now.\u003C/p>\n\u003Cp>So it's a for let n of, can't do n.\u003C/p>\n\u003Cp>Andrew: Yeah. If you just change n to to next moves or something like that or or yeah. Yeah. There you go.\u003C/p>\n\u003Cp>Kevin: Sure. Oh, I see. So for so next, I'd rather that's fine. For Yep. So I can use the shorter version inside the list.\u003C/p>\n\u003Cp>I'm about to use it, I think, a reasonable amount. So for every single value in there, is it one of those? It's all we care about. Let's have a think let's have a quick think about how we're gonna achieve that. Okay.\u003C/p>\n\u003Cp>So for every single one, we're gonna say, does next possible Does value in one array appear in another?\u003C/p>\n\u003Cp>Andrew: Hey. There we go. Nice little vanilla JavaScript there.\u003C/p>\n\u003Cp>Kevin: Absolutely. Include some and include. Some text element against the test function that return. Yeah. It's great.\u003C/p>\n\u003Cp>Cool. I don't think this is, oh, but it's that's no. It's that same thing again with objects. So I think the object nature of it, like, object comparisons are are specific. Yeah.\u003C/p>\n\u003Cp>It's a find and a size I think this is actually flipped.\u003C/p>\n\u003Cp>Andrew: What are the responses? Did they say it actually works?\u003C/p>\n\u003Cp>Kevin: I think so. That's a bit of a it's a bit of a big big example. Got it. Got it. Lovely.\u003C/p>\n\u003Cp>That's the answer. So I'm gonna just I'm gonna just pop it here and comment it out so we just have a little reference at the point where we're writing it. So this is an array. Let's consider this as our possible next steps. Yeah.\u003C/p>\n\u003Cp>So we wanna say, for, matches r.sum. It's not called r, is it? It's called next possible steps. Possible\u003C/p>\n\u003Cp>Andrew: next steps. Possible next steps.\u003C/p>\n\u003Cp>Kevin: Possible. I'll fix that in a moment. So sorry. The sum method of an array test whether at least one element in the array passes the test implemented. Okay.\u003C/p>\n\u003Cp>So next possible did I make a typo?\u003C/p>\n\u003Cp>Andrew: Yep. Possible next steps, not next possible steps.\u003C/p>\n\u003Cp>Kevin: Just Thank you.\u003C/p>\n\u003Cp>Andrew: It's what I'm here for\u003C/p>\n\u003Cp>Kevin: Possible next steps. And it immediately changes color to be like, yeah, you can spell now. Thanks. Possible next steps dot sum item. We'll do that again, and it's item dotcoords dotcoords.\u003C/p>\n\u003Cp>Dotxmatchn dotx. Dotxanditem.coords.y. For n of next. Is this is this how is this really I I think I I think there's some redundancy in here, actually. Yeah.\u003C/p>\n\u003Cp>What? Yeah. This is all we're this is this is trying to, like but we're already in a loop. We're already in a loop. So all we wanna know was, I've got it.\u003C/p>\n\u003Cp>Matching collision. Collision. Is that how you spell it? Collision Yep. Equals possible I got it.\u003C/p>\n\u003Cp>I got it. I got it. I got it. Next steps dot find and because we just wanna find does does one match. Right?\u003C/p>\n\u003Cp>And what we wanna know is step in there, is there a value where the cohorts dot x are the same as n.xandstep.cowards.y equals m.y. If this returns a value\u003C/p>\n\u003Cp>Andrew: It works.\u003C/p>\n\u003Cp>Kevin: Then that yeah. Then that collision it's gonna be a little bit of a long winded one to test because also we're gonna have to go we're gonna have to go back through the logs. This one's gonna actually be really painful to test because in theory, if it works, you know, but, so what this will return is a possible next step. And so what we will then do is if this this will have the value of possible next step in it. Mhmm.\u003C/p>\n\u003Cp>If it matches, so what we will then do is say if collision, because it will be 4 to undefined, like, some faulty value if if there's nothing returned in there, then possible just get myself a few characters because I think I'm gonna go quite far to the right here. Possible next steps equals possible next steps dot filter, and we wanna remove this item. And it's this. It's this logic right here that we already wrote, but slightly different. So we wanna say we're s.coawards.x, where collision by collision.cohorts.x.\u003C/p>\n\u003Cp>Oops. This one here, where it is the same as collision collision. Am I spelling it right? I I swear I swear I swear I\u003C/p>\n\u003Cp>Andrew: That's right. No. You're good. You're good.\u003C/p>\n\u003Cp>Kevin: I'm literally just like, you know, when words start to look wrong.\u003C/p>\n\u003Cp>Andrew: Yep. No. But it's catching it. It's catching it, turning it purple if you're right.\u003C/p>\n\u003Cp>Kevin: And this is the same as collision dotcoorg.y. Okay. I think I think this is\u003C/p>\n\u003Cp>Andrew: it.\u003C/p>\n\u003Cp>Kevin: Then oh, no. There's one more step. So now possible net, possible next steps is a shortened array which has just items which are viable. Yep. Just items which are viable.\u003C/p>\n\u003Cp>Let's just have a think because we we don't wanna\u003C/p>\n\u003Cp>Andrew: What is that actually gonna look like, though? Do we know right now?\u003C/p>\n\u003Cp>Kevin: It it will be an array of objects that look like this.\u003C/p>\n\u003Cp>Andrew: Is it just gonna be a same case?\u003C/p>\n\u003Cp>Kevin: Yeah. Yeah. But just a shortened a shortened list. Yeah. Now what we actually wanna do now I think about it is we kind of want the others, which I've just realized we want we want the other items.\u003C/p>\n\u003Cp>We want the ones that are being removed. So Oh, to So removed from his move. Yes. Yes. Yes.\u003C/p>\n\u003Cp>Yes. So let make false. This is we're starting to get a little hunky now in terms of, like, code quality, but, like, it's fine. So if collision we don't wanna screw around with possible next steps. We don't care about that.\u003C/p>\n\u003Cp>All we're gonna do is add collision dot direction to make force, and we're not even gonna do that. We are gonna just straight up say is move safe dot left, and the value of left will be in collision dot the equals false. And that's the direct we are making the the value that we came in with. Yeah. We're setting it to false because we're gonna collide.\u003C/p>\n\u003Cp>And I'm not sure if this is valid, syntax.\u003C/p>\n\u003Cp>Andrew: I will find out.\u003C/p>\n\u003Cp>Kevin: We will find out. I might I might just save ourselves some effort here. Commission dot the okay. Alright. Sorry for bringing oh, don't don't play this game.\u003C/p>\n\u003Cp>I, I'm sorry. I felt like that wasn't an overly, like, collaborative exercise, and I'm sorry.\u003C/p>\n\u003Cp>Andrew: That was it was wonderfully collab I don't know what you're talking about. See, my favorite collaborative experiences are the ones where I can nod along and say yes, And I can I can help pick out when you've misspelled something? That's, I feel like I've I've contributed in my own YouTube. I don't\u003C/p>\n\u003Cp>Kevin: know don't know what that little error is at times. Anyway\u003C/p>\n\u003Cp>Andrew: It's weird.\u003C/p>\n\u003Cp>Kevin: Let's and this one is gonna be a little bit of a hard one to to test. So we're gonna just have to watch watch this carefully. The snake's going up the whole time.\u003C/p>\n\u003Cp>Andrew: Okay. So there's a bug somewhere.\u003C/p>\n\u003Cp>Kevin: There's a bug somewhere, but we will be able to See debug it. Oh, it all died here. Un un there's a problem in the setup of the globals. There's a comma.\u003C/p>\n\u003Cp>Andrew: Change in here.\u003C/p>\n\u003Cp>Kevin: Comma.\u003C/p>\n\u003Cp>Andrew: Comma. Those commas.\u003C/p>\n\u003Cp>Kevin: It's always the commas.\u003C/p>\n\u003Cp>Andrew: Grammar's out to you.\u003C/p>\n\u003Cp>Kevin: I'm loving I'm loving now that we don't have to touch the code editor at all.\u003C/p>\n\u003Cp>Andrew: Yeah. Agreed.\u003C/p>\n\u003Cp>Kevin: Okay. See. So where K. Avoid head to head. Fine.\u003C/p>\n\u003Cp>Unexpected token line 8. Great. Okay. 8. There is an extra bracket here and an extra bracket.\u003C/p>\n\u003Cp>No. No. No. The one at the end is the one at the end is correct. There's an extra bracket here too.\u003C/p>\n\u003Cp>Great. Let's see if that does it. Yeah. I I literally cannot believe we're okay. I was gonna say we're getting it to work.\u003C/p>\n\u003Cp>Andrew: We're getting things are happening. We're debugging, and that's the important thing.\u003C/p>\n\u003Cp>Kevin: Alright. Snakes is not defined. Okay. Not a problem.\u003C/p>\n\u003Cp>Andrew: What's the I mean,\u003C/p>\n\u003Cp>Kevin: this is good. Oh, snakes. Fine. It's all really small, like, little errors, quite well defined, quite quick to fix, allegedly. Alright.\u003C/p>\n\u003Cp>Alright. Alright. I need everyone to know at the time of recording, it is 11:30 at night. Snakes dot shift is not a function or its return valuable value is not iterable. Right.\u003C/p>\n\u003Cp>That's that's fair. And that is here, snake dot shift. So what we're gonna do is just it really doesn't matter. Like, snake star shift, and I I'm assuming it that's how it like, it needs to happen on its own line first.\u003C/p>\n\u003Cp>Andrew: Kevin, if this is what you programming at 11:30 at night does, I don't wanna see you programming when you're fully rested and awake.\u003C/p>\n\u003Cp>Kevin: Well, here's here's the joke. I'm I'm never fully rested. I mean, children. Children. Yeah.\u003C/p>\n\u003Cp>Fair enough. Well, it's going it's going up, but I also didn't set that up correctly. But let's let's keep having a look at the logs. S is not iterable. Okay.\u003C/p>\n\u003Cp>I think I've misunderstood how shift works. 1st element. Oh, it returns the first element. I don't want that. I I want to return them.\u003C/p>\n\u003Cp>Right. Right. Right. It returns just the element. It pops out of the top, but I want everything else.\u003C/p>\n\u003Cp>Andrew: Oh, everything but. Yeah. So what's the option of the shift?\u003C/p>\n\u003Cp>Kevin: It is to do the head, avoid head to head. So it is to do the work. So it's to do the shift snake stop shift, and then I think it might actually be to put snakes back in there. Yep. But I'm not sure if snakes is re I'm not sure if that will work.\u003C/p>\n\u003Cp>I think it will. Okay. I think it's not doing that anymore. This is obviously not the test, the,\u003C/p>\n\u003Cp>Andrew: we need to But we're not we're we've got no actual errors in the code that we've well, that we I use we very liberally. The code that has been produced in that final card is working and it's definitely a little bit slower now. So I think that we're definitely getting a, there's a lot more compute going into this, which totally makes sense.\u003C/p>\n\u003Cp>Kevin: Yes. Okay. We're we're still going. Woah. That was a okay.\u003C/p>\n\u003Cp>Okay. It did end up giving up. Why? Oh, because there was no viable choice. Yep.\u003C/p>\n\u003Cp>So that's Which is yeah.\u003C/p>\n\u003Cp>Andrew: Wow. Hey. We wanna move fast oh, sorry.\u003C/p>\n\u003Cp>Kevin: What do you mean?\u003C/p>\n\u003Cp>Andrew: I was gonna say if we wanna move faster for the head to heads, put multiple snakes in more than just one.\u003C/p>\n\u003Cp>Kevin: Okay. I I think we I think we got a snake.\u003C/p>\n\u003Cp>Andrew: I think we did too. I think it's working.\u003C/p>\n\u003Cp>Kevin: I'll stick 2 hungry bots in that There we go. Cough.\u003C/p>\n\u003Cp>Andrew: That should do it.\u003C/p>\n\u003Cp>Kevin: Should have hit start game first, so it did its job. Okay. Head to heads are obviously gonna be hard to to start.\u003C/p>\n\u003Cp>Andrew: Yeah. But we can\u003C/p>\n\u003Cp>Kevin: kind of see we can kind of see here when it ends it ended. Okay. So now we can watch the whole thing.\u003C/p>\n\u003Cp>Andrew: Yep. So he's not what's he turn why is he turning? Was there a safe move there?\u003C/p>\n\u003Cp>Kevin: There was a safe move. How do I go back? Pause back. No. There wasn't.\u003C/p>\n\u003Cp>Andrew: Nope. There wasn't.\u003C/p>\n\u003Cp>Kevin: It got itself into a corner. Fine. I'm confident I mean, I'm not confident enough. I do that were there any head to head opportunities? And I didn't see one, did you?\u003C/p>\n\u003Cp>Andrew: Nope. No. But I think I think we're not getting any errors at that final stage, and I think that's a good sign that the logic in there makes sense.\u003C/p>\n\u003Cp>Kevin: You yeah. Yeah. But it might not be doing what we want. So what other few do you think 11 by 11 is still a good size, or maybe I should make it small?\u003C/p>\n\u003Cp>Andrew: Make it small, and then we can see faster. And, yeah, give that a try. Yeah. Yeah. There we go.\u003C/p>\n\u003Cp>Yeah. Run that and see what happens.\u003C/p>\n\u003Cp>Kevin: Oh, it's it it went on for a reasonable length of time.\u003C/p>\n\u003Cp>Andrew: K. So there looks like\u003C/p>\n\u003Cp>Kevin: No. Our snake didn't didn't need to didn't have a head to head moment. Well, I mean, it avoided a potential head to head moment there, but But it's I'm happy I'm happy in no. Yeah. Because we'll never know, will we?\u003C/p>\n\u003Cp>Yeah. We'll never know.\u003C/p>\n\u003Cp>Andrew: We'll know if we play enough games over a long enough period of time, but I think the code is there.\u003C/p>\n\u003Cp>Kevin: And we would know it doesn't work if there is a head to head, but they don't happen often enough. Hey. We leveled up our snake.\u003C/p>\n\u003Cp>Andrew: That's solid. So not only move it to a different platform where it's running, same logic, and then adding that extra step and super visual. Super visual. I'm very I'm very,\u003C/p>\n\u003Cp>Kevin: very satisfied. I'm very satisfied by it that working. Awesome. I think that brings us to time. I mean, we we have to be at time at this time.\u003C/p>\n\u003Cp>You gotta go to sleep. I've got yeah. My kids will be up in 5 minutes. So, awesome. Thank you for for for for doing this again.\u003C/p>\n\u003Cp>Andrew: Yeah. Thanks for joining us, everybody. I feel like I'm glad that you were driving this time because had I been driving, this would have not gone as smoothly in any way, shape, or form. So I feel like this was gonna be\u003C/p>\n\u003Cp>Kevin: be silly.\u003C/p>\n\u003Cp>Andrew: But I I am excited to eventually drive when we, again, in episode 3 or 4 when we start to implement some other technology. So do we know what we're doing on our next episode yet, or is it all\u003C/p>\n\u003Cp>Kevin: In our next episode, we're gonna stay in Directus, and we're not gonna be leveling up our snake. But given that Directus is backed by a database, we're gonna start actually storing data about games and then seeing what we might be able to learn with Directus Insights.\u003C/p>\n\u003Cp>Andrew: I love it. I love it. That's gonna be an awesome next episode. So, we've got a snake. You've seen how it works.\u003C/p>\n\u003Cp>That's all of the steps for the starter snake that are worked out in there. It's actually just a functional JavaScript snake. You could pull all of that logic out. Well, most of that logic out and just put it into a JavaScript Express server and just go to town. But, but we've got it working in here.\u003C/p>\n\u003Cp>And so now let's do a little bit more. Let's see how powerful you can make Directus with Battlesync data. I'm very excited.\u003C/p>\n\u003Cp>Kevin: Yeah. This is awesome. So until next time, this has been ready, set, battles, stake.\u003C/p>\n\u003Cp>Andrew: We're getting better. We're getting better.\u003C/p>\n\u003Cp>Kevin: Some some would say. Some would say. Till next time, folks. Bye, buddy.\u003C/p>","Hello, and welcome back to we've got a catchphrase, Andrew. Are you ready? Ready, set, That's easy. We got 4 more We got 4 episodes. Exactly. We're we're we're we're cutting our losses for this one. In this show, we are continuing to build a Battlesnake, and progressively enhancing it, using Directus and Devcycle. My name is Kevin. I work at Directus, which we'll be using a little bit today. And My name's Andrew, and I work at Devcycle, which we'll be using in a future episode. And this is Battlesnake. Yeah. Give us the give us the 92nd for people just dropping in now. What is Battlesnake? Alright. Battlesnake is the way you wish that you had learned how to, program in a new framework or language from the very beginning. And now as a senior developer, Imagine you were playing the old classic snake game where you go around a board trying to eat food and survive. Now transform that into a programming game where you have a web server that's communicating with the Battlesnake game engine to communicate through code and tell your snake whether to go up, down, left, or right, and to try and survive as long as possible. Yeah. And we do that by implementing a web server, which implements the Battlesnake API. We've already had one episode of Ready, Set, Battlesnake. So we're not going to recap everything we did. If you are still a little fuzzy on how this works, it will probably become clear. But if you're completely fresh and you're like, I'm lost, go watch the first episode and watch a struggle through building the Node. Js starter snake. But today, we're gonna further build on top of that snake. Last episode, we built a battle snake based on the Node. Js starter, and we implemented here we are. And we implemented some logic. That logic, what what did the logic do? The logic stop listening dying by going hitting into walls. That's good. Then we got the snake to not hit itself or any other snake parts, because that also is instant kill. So that's all our snake does. Right? Yeah. Yeah. That's all our snake does. And we're probably not gonna make our snake much more clever today, but we are now going to introduce a new tool into the stack and get it out of Replit and into another environment. Anything else you think that's important, Andrew? So the great thing is you can follow along with kind of all of the base code for all of this over on the GitHub repositories that exist around. Snake. So if you're watching this and you're like, I want to do it in my own language or framework, you can go and check out all the awesome Angular projects that are over there. So actually, let's take pause for a moment because this is important. What is a Battlesnake? So you have to implement a web server that has a few specific route handlers. The a few specific endpoints. Firstly, this just route route. Right? And all this one does is it returns information about the snake, metadata about the snake. And we'll play around with that a little bit more today. The mode then we have the start endpoint, which, Andrew, I think that's the before the first move in any game. Right? So we got our start end point, which is great. We've got our root end point. What is the kind of most important endpoint of all the endpoints though, Kevin? It is this move endpoint. As we discovered last time, the move endpoint, every single turn of every single game will receive a request with the state of that game and the board, which includes every other snake, all the hazards, all the food. You have 500 milliseconds to respond with a string up, down, left, or right. And that is this is the most important endpoint. But you there is also an end endpoint, which is what you would potentially use to, like, wind down any resources or, you know, do any analytics storage, you know, and you just do it at the end. So we have these 4 endpoints that we need to implement. Move is is kind of the most critical. I think only move in the the root are required for any given snake. So that's worth noting because that's gonna be important. So what we're gonna do today, we are going to be building this snake inside of Directus. If you've not heard of Directus before, I hope you have because you're watching this on Directus TV. But Directus is is a back end toolkit to build applications. It has 2 parts. One part which is all around API generation from your database and asset storage, and the other side, which is a web application to kind of work with that data. There's a whole bunch of other stuff in there. So we're gonna be implementing a Battlesnake inside of a director's project. So we'll we'll we'll kinda see how this shakes out over the next over the next little while. Now originally, until about 5 minutes before we started recording, I thought that we would perhaps use, Directus' automation UI tool in order to build our snake because it is it feels quite viable on the surface. It does. And then I realized it ain't it ain't viable. And the reason it isn't is because one of the triggers that can begin a flow is a webhook, an inbound webhook, but it is unique, and randomly generated at the time the flow is created, which means we can't implement a root endpoint and then slash move, slash start, slash end. And that's gonna be a challenge. So rather than just trying to to power on through that, rather than sorry, rather than just trying to power on through that, we instead are gonna build an extension. Flows is effectively a UI layer or a UI version of, hooks of hooks. So, you know, we can we can do it in code. We also have endpoints, which is probably more suited for this where we can literally create complete endpoints, and this is a no doubt. So we'd probably lift and drop a lot of our logic. This might end up being a bit of a lighter episode, but there would be nothing wrong with that. But I've done the thing. I've jinxed it. You have. I did it last time, though. So in fairness, we're we're each one in, and and so I feel like Indeed. This is way to do it. Indeed. And so we're also we're gonna be implementing some some, like, very like, this whole rate limiting thing has caused us to have to think about a lot of other technologies as well too. Right? So it's not just this concept of of sort of how we're gonna implement Directus. It's also You're right. How it's gonna be able to interact with with the Battlesync platform. Right? Yes. So here here's the plan. Here's what I think the game plan is. We're gonna spin up, direct us locally using Docker. And that is an environment in which we can build extensions. And we could then just deploy it somewhere and be happy days, you know, deploy it on some some web service, or we could just, like, open up a tunnel to our local machine. And ngrok is kind of the tool that we've as as developer educator types have have historically jumped to, so I think it will be the tool we use today. I also found out today I'm apparently still paying for it on a grandfathered plan that gives us the pro features. So, you know, I think it's one I might wanna Let's do it. Get off the card, but I'll use it today. I saw a I saw a really great, idea from someone recently just to, like, automatically, not only list all of the subscriptions that you have, but actually unsubscribe you from something if you haven't accessed it in a while. And I was like, that's a brilliant idea because I have so many things to subscribe for that I'd like to remove my credit card from. Yes. But I'm happy in this moment in this moment that it's working. Now one thing that's really nice here is the extension entry point. If we look at this, example, the way it's built is you give an endpoint an ID, which becomes the kind of root path. So this would be like our director's project slash greet, and then you can create the root, the slash move, the slash start, the slash end, and so on. So this part should be pretty straightforward. Let's let's see, and if the docs end up being misleading, that's literally my job. So it's it will be incredibly self inflicted. So we'll we'll see. So we're gonna start by following the Docker guide for getting started with directors. No. We're not. We're gonna look at the self hosted quick stuff, which which is super smooth. It is a very smooth Docker process, I will say. Like, setting it for the first time, it was it was chef's kiss. I like it, and it's pretty, we we kinda sometimes refer to it. I mean, we will use different words, but I call it like full fat. This is the full thing. There's directors cloud, which is like, you know, a a managed platform. But this if you self host it, it's full fat, which is really nice. So I already have Docker running in the background and I already have a code editor here, completely completely blank code editor and a new directory, which I've opened up. So I think we're just gonna we're just gonna crack on straightaway in here. So first thing we need to do is create a Docker Compose file. It's a Docker dash compose dot YML file that describes all of the settings that we have with directors. We may need to fiddle with this later, But for now, docker compose dot yml. And let's make my font size significantly bigger. There we go. We can copy and paste the value in here. Keys and secrets are meant to be random values. They're used for, identifying, your account in, like, a horizontally scaled setup and the secret is used for JWT validation, and we don't care about that right now. So we're just gonna leave those. Hunter 2 is when I set up projects my default. So we're gonna Good to know. Good to know. Good to know. Good to know. I'm searching around for past Kevin projects and just trying to break in. Absolutely not. And no. No. That will that will not be good. We're gonna use, yeah. Sorry. Go on. No. I was gonna say an interesting note here is I discovered with the self hosted version, you can't use a fake email address. You can't use dot test. It won't actually accept it as an email address in the platform. Yeah. Interesting. Interesting. I was when I was building out that railway sort of like starter kit for it, I was trying to just set it up with a test and it would not it wouldn't go. You had to put it in an actual address. Interesting. As in, like, it follows the structure of an email address, but it isn't real because it should do. That should be fine. I think it was because it was a dot test, t, like, t Yeah. But that that's they're legit characters in an email address. So anyway anyway. Things that's important yeah. One thing that's important to know about directors is it doesn't provide a database. It connects to an existing database. We're gonna use this lightweight file based database called SQLite. We're gonna point it at the file. And on first run, it will create the file for us. That isn't Directus, that's a SQL like database that Directus connects to. So just a little bit of important distinction there. 10.8.3 is the latest version at the time of recording. By the time of publishing, that will not be the case. I think we might be on 10.9 by now. That's pretty exciting. Yeah. All good Database persistence, file persistence, extension persistence. WebSockets are enabled even though I'm not sure we're gonna use them. And instead of, continuing to talk through every line in excruciating detail, I think we can just write Docker Compose up. It should kick off. Great. It's attaching. Boom. Boom. Boom. Doing a bunch of stuff first time and running now on local host 8055. So if I hit local host 8055, brand new directors project with randomly auto filled credentials, which are not I think I have to move them out of, yeah, manage passwords, but that's not for today. I bet you that was a hunt to 2 password too. But this is a blank director's project ready to go. So that was that was great. What else is important here? If I open up oh, I haven't opened up the folder. This is annoying because obviously the terminals attached to it. So let's just, let's just stop that running. Let's open this snake folder here. I think I might have been able to add folder to workplace. We're here now. Let's just run Docker compose up again. Great. And just refresh that and everything should still be fine and dandy as it is. Now the first time we ran that, it created some directories here on the side, which I think are gonna be quite small right now on people's screens. But as a database directory with our SQL like database and extensions directory and then uploads directory. Grand. So we have this direct as project and it's great that we, you know, can go in and create, you know, collections in our database and upload files and create dashboards and all of that stuff. But really, all we care about is implementing these endpoints from our Replit and bringing them over. So let's go back to the docs. Let's talk about building extensions and get that get that game get that game underway. Directus has a whole bunch of extensions. Some of them affect the UI that that web app that we just had open. Some of them are API side like custom endpoints and hooks. Yeah. As it says here, hooks are similar to flows, but they do not have the UI, which is in the director's data studio. I think to create an extension, we're gonna go I'm sounding a little uncertain. I don't know why. Like No. I love it. It makes me feel much better about about episode 1, now that you're feeling unconfident about the thing that you deal with every day. I feel like imposter syndrome is real, but at least you haven't had to Google how to make a JavaScript loop yet. So Oh, good. The episode is young. Run so I'm gonna run this MPX create directors extension. I'll pick an endpoint, pick a name, directus dash extension dash, snake. Snake. Snake. I it doesn't it doesn't really matter. We'll use JavaScript. We'll auto install dependencies, and we'll give it a moment to scaffold that extension for. This is a nice CLI. Like, I gotta say, this is a very enjoyable experience. Yeah. There's still work that needs to happen, and it is happening. But, yeah, this boiler plating isn't too bad, actually. I, yeah, I I I quite like it. So inside of our extensions directory, we have this new snake. We have source. We have this index dot JS. Grand. Now what we want and so this, I think I think this might be available, I think, at directus dash extension dashnet like slash. It's a little bit clunky, but we're not going to use this format. It doesn't really matter. We're going to use this. We looked at it earlier, this alternate format, where we can do all of the subroutes straight in here. So I think I think I can just copy and paste this. Kevin, is this just is this just vanilla, or is this React? Like, I this looks very clean, but it doesn't Yeah. So this so the front end of Directus is a view app, but this what you're seeing right now and ding, ding, ding, I think you've just spotted it. Endpoints use oh, directors also uses the express router. So it is very much what we did in the first night, which is why being able to lift and drop it is gonna be gonna be quite nice. It's so clean. I dig it. I love this. This is beautiful. Yeah. Thank you. So we have snake slash snake intro snake goodbye. So let's get this running. So we're gonna c d into our Directus snake directory and we're gonna run npm run build. That's built the extension into a disk directory. And now we need to restart Directus now. I think I think it might be broken, but I think we can get it to I think we might be able to get it to, like, auto, like, load the the changes. What am I looking for? Self hosted config. Okay. So we are looking for extensions, extensions auto reload. Here we go. So Boolean will automatically reload extensions when they have changed. So we're back to our Docker Compose. We'll throw that in and make the value true. Maybe that will work. Maybe that will work. So run Docker compose up again. It's now gonna use this Docker compose file again with this new variable. Let's take a little look through the logs because hopefully yes. It says loaded extension, direct us extension snake. And then the hope is if we run npm run build again, it hopefully would have auto loaded, but that isn't the case. That's fine. That means we're just gonna have a slightly clunky development experience. So I'm gonna have to control C up enter, you know, off the baton. That's fine. I gotta say though, like I remember like outside of this, I feel like we've gotten very spoiled in the development world now. Like, that used to be just the way that you did it. I used to use some. Dragging files with Filezilla or Cybernet. Yes. A 100%. Yeah. Yeah. I that's so I thought that's how you did it. I genuinely thought that that was the only way to do things. No. Yeah. But not here. Like, this this, I'm not quite sure what the deal is. And by the time this is published, maybe this won't be the case anymore. Just for the last couple of versions, if you're running under Docker, I've noticed this environment variable isn't working. Our team's aware that's fine. I just wanna try it on the off chance there. Yeah. Whoops. So if we hit 8055 snake, we get the hello world. If we try snake slash intro, we get the nice to meet you. So these are endpoints that are returning stuff, which means we should now be able to lift and drop our snake pretty expediently. Yep. So what we what are we gonna do here? We have handlers dot info for the root. Yep. So you're gonna grab that. Return that. So I am just gonna make this slightly more verbose so I can space it out nicely. Yeah. So, can you bump up the size a little bit too? A little bit more? Of course. Is that any any better? That's that's much better. Yeah. Lovely. Rez got Jason. Yep. Do you need to return it? I I believe so. Yeah. Cool. Nice. Yeah. I think everything you need to return. Whoops. Got a little too a little too direct there. Let's, let's while we're here, maybe let's do some customization of the snake. That feels good. Yeah. That's good. So head over What color do we wanna make it? Oh, what's the direct is it a direct purple? Is the is the color purple that you're using? It is a purple. And, honestly, my lot are gonna kill me for, like, not remembering it, but I think it's, like, 6 6. I wanna say it's like 6644 f f. I like it. Let's see. We'll find out momentarily whether this is Yes. Yes. That's right. I got it. 6644ff. I logged into my Battlesnake account. We're gonna look at the customizations. And these are the customizations we can have for our snake. Andrew, you you may do the honors from the ones I have. Oh my goodness. So I'm a big, big fan of mustache snake, but I'm pretty sure that there if we scroll down, there may be a rabbit in here now. I thought I thought we had play around seeing if I could get the director's Bunny to look good in one of these, and I had just I couldn't with, like, the it has to be full width on the left. The Bunny, let's take a look. Do we have a Bunny? If not, I know he exists in the backlog. So we may we may be able to get Bunny out at some point here, or he may be locked down. Either way, if not, if we can't get our bunny Oh, I have 200 I have 200 coins as well, so I can unlock any one. Oh, so what do we see? Do we see any that we really like? So we got chicken. I quite got I quite like this dragon head here. I have to be honest. Yeah. Take the dragon. But is it was really funny. No. They're all 200. I don't see a bunny. No bunny. That's disappointing. So let's go for dragon. I feel like that dragon head Well, hang on. Let's let's look at the tails too. Let's let's, you know, maybe not spend all of our coins at once. True. No. I think I think the I think I like the heads. Well, yeah, let let's do the dragon. I've done it. Right. So the head we want is dragon, and the tail is So, I mean, let's go I think cosmic horror's tail is pretty fun. I like that. I like that. Okay. Cosmic Chorus. So that's our that's our snake. And once again, actually, should we get all the logic in? I think we should do this bit by bit. Like, we could now go off and make sure that this works, which is gonna be a whole task in itself. Does that feel okay? Yeah. Yeah. I think let's do that because then once we know it's working, then we can kinda go through. Is that what you're saying? You're thinking, like, let's go in and actually add it to the platform. I am with NVIDIA because we gotta get we gotta get ngrok set up now. Yeah. So do you wanna explain what ngrok is while I get sucked in? Yeah. Yeah. Absolutely. So if you've never used ngrok before, it is a way to publicly expose your local, development environment to the outside world, whoever you would like to be able to access it. So if you often use, say, Gitpod or, Codespaces, there's often the way that you can make that public u that URL from private to public. That's fundamentally what this ngrok, endpoint that it's giving you is doing. But you can do it on a on a, case by case basis. And you can kinda keep it in one place, and you have a bit more control over the power of that and who can see it and all the rest of that stuff. It's our and it's really great, especially if you're, like, say, somebody that's building a site and you wanna see what it looks like on mobile. I think they might still do it. But I know there's some services that I've used that it would automatically generate a QR code from the n grok address so you could just, like, scan with your camera and then view on mobile. Doesn't impact us here, but, really, really good. And ngrok has taken a lot of steps to make things more secure as well. So it used to be that sort of anybody could spin up an ngrok address and there was no issues at all. And they've added a lot more security and ensuring that the things that need to be in place are. Yeah. And I will say around ngrok, if you are, on a local development environment on a Windows device, so a big Windows developer, there's a service called Laragon, which is like, an alternative to like a LAMP stack. And it has ngrok built in. So in there, you can just click share and then it automatically will, like, push that out to ngrok that you could share with people. But, yeah, powerful, powerful service. So there's a little bit of a moment here. I'm not quite I don't quite have the memory of how this works. I think it works like this. I think based on what I've read, So you do ngrok HTTP, the port, which is 8055 and then dash dash domain and then your your domain. No. Reserve this name on your dashboard. You don't need to do that. It'll automatically just generate a random one for you. So just No. Yeah. But I don't want that because I'm gonna have to keep updating it every time it restarts. I think I have I have a static. I have a static. Oh, I see. Like, because I because I'm on this plat this old plan. Right? Yeah. So I wanna work out Yeah. I haven't touched ngrok in in a little while. It's funny, like, it stopped working in that environment. There was, like, they hadn't updated the way that was integrated, and they added an authentication key. And so, it switched things up. So your guess is as good as mine. We're gonna get oh, alright. Here we I mean, yeah. I suppose I could just do the help, right? So start engrop. That's cool. Basic auth. Cool. OAuth 2 cool. Here we are. Use it the same time by using dash dash domain. But, and I thought maybe I, maybe I. What's your domain? I thought it was that. That's why I that's why I paused. So if I look at domains, there it is. Unless I have to do the whole domain. Try the whole domain and see what happens. Because it because it did just say the ngrok.io is only for, like, legacy accounts. Yep. And that was it. Yeah. There you go. Great. So we've now got we've now got engrok rocking and rolling over here. Great. So now what we're gonna do is go over to battles go over to Battlesnake. We'll create a new snake like we did last time. So this is ready, set, battlesnake. Ready, set, battlesnake. Cake. Yes. That's it. Nope. Nope. K for Kevin. That's it. I love it. Beautiful. So https phzn.ngrok. Io/snake. Engine region, Netherlands or Netherlands? Yeah. Well, Europe. So yeah. Sure. Yeah. Programming language, JavaScript, and platform Director. Should be there. Directus. Now my question here is, does the logo look good? Oh, let's see what happened to it. So I used the I got them to add Yeah. It doesn't look too bad. Yeah. It's an old it's an old one. So we'll fix it by the next episode, but it's good. It's just colors just colors and details. But yeah, that's that's our little bunny rabbit boy. Unknown API version. So there was an error. Yep. Even though we copied You should paste it. So what I would say is, I think I think I think I think you might need to just reset. Because this used to happen on Reset reset what bit? Like oh, sorry. Reset everything. So just, like, ping it again. And if it doesn't work, then actually start running, like, restart your direct to server. Okay. This used to happen with replit all the time is it would get this, and then it would just need a couple minutes to start syncing up in the right way. I don't know what that mean. I don't know what that means. No. It's no. That's right. So if I hit that endpoint Yep. Oh, it still says hello world. Oh. Why? Oh, why? Because we needed to rebuild the extension, then we needed to restart director. So there you go. That was our little people. What are you doing? No bill. What what what are you doing? Hit ping. Perfect. There it is. Oh, battle snake. It looks sick. Yeah. I love that. Yeah. So now we have a snake. Right? But we haven't implemented any other endpoints. So this is the time to do it. Now this is the point where maybe, like, we split out the move logic into a separate file to stop it. No. Do you wanna know what? I actually wish that the move logic was in with everything, even in the JavaScript starter snake. I understand why it's not, but I actually think it's way better to have everything in one place just so all of your routes are are there and you're you're able to change that data. So I think I think let's do exactly what we That works for me. So there we there we go. And I am just gonna shrink this slightly and shrink this slightly so we can get the replet up and kind of do a little do a little moving from one to the other. Now can I, can I shrink this sidebar down? Yes. Perfect. And maybe even shrink that. Okay. Rock and roll. Get rid of that. Oh, and get rid of that. There we go. So first thing we're doing is is move safe? So move, get some data in. Let's console log the request just so we can make sure, like, is it in request dot body? I I mean, I'm assuming it's in request dot body. Yep. Let's let's just double check that, and just respond with hello. I like that after our last adventure where we had to disappear and through the magic of editing come back, we've decided to now add console logs earlier as we go so we can save ourselves some trust down the road. That's my style. That's my style. Discum. Okay. Experiential. This is the point where and these these logs are gonna get noisy in a moment. Yep. I may even take them down inside until we need them, and then I can make them smaller still and then I can bump them back up. So what are we gonna do here in Battlesnake? We have a Battlesnake. Let's create a game, create game. Add ready set Battlesnake standard board 11 by 11. And that's good start game. Good. 404, 404, 404, 404. Well, great. So but here's the thing. If you didn't watch episode 1 so here's the interesting thing. Is technically, technically in Battlesnake, you can actually win if you 404 or have a 500 error every single time because you have to set a default move that your snake makes. And I think that there's something, I mean depending on the implementation, I think there's something cool that you could do about actually having a way to change that default. Anyways, but then game's still in progress? Yeah. It's been weird and slow. It's, like, not actually still in progress, but the platform's taking a bit of time to catch up with, I think, what's actually going on. So Why is it 404ing? Root to talk. Kevin. Host. So we'll rebuild that, and we'll restart directors. I was using the wrong method right here. It's a post request to slash move. Okey dokey. So create game, add ready set battlesnake, hit start game. That should be better. There we go. So move gets hit. Oh, so this is it. So the data comes in. Yes. Req dot, req dot body. Fantastic. So now let's move all of this over. I might do a little bit of cleaning up as we go. It'd be interesting. Yeah. Never mind. Let's let's get to it. Let's get to it. Go ahead. I was gonna say it's it'll be interesting if we if we wanna add the start and end point and log what's coming through there. I think there's something interesting in that as well, just to kinda see what that looks like before the logic is implemented. Because it does get really busy once the logic is implemented. At least right now, we're just getting those 404 errors or, like, the basic logs. It's a start. And is it a post request? I think it is. Yep. Yep. Post. We probably should respond. Right? Yeah. And and it'll it'll get angry with us. But And and. Yep. And just because they are gonna get in the way, I'm just gonna pop them down there. Okay. Let me just, cough. Let me mute. Yeah. And so what's interesting here is so I think we we said this earlier, like, the start and end points. You don't really need them for anything, but they're useful if you wanna implement some more serious logic, which we may do sort of with our direct as Battlesink down the road. So you'll see here once we rebuild and then save again, you'll actually be able to kinda see the stuff that comes in Yeah. Through there. So we need the width The height. And the and the height and the Head. Snakes. I think that's all we need. From rec.body.board. Right? Yeah. It's all in this thing called board. Yeah. Great. And the other thing we need is my head, and I am just gonna copy and paste that little convenience there because that's that's wicked. If you by the way, if you really don't wanna have those 2 constant, like, lines that you have there, if you're just, like, really focused on compressing, you can technically pull it from that sneaks variable that you're defining in there. It's just a little bit more more to do it. Absolutely. Yeah. Just can't I mean, because be bothered. Can't be bothered. Right. So so so we've got the we've got the width and the height. So first thing we were doing is don't don't move out of avoid walls, and we're just gonna copy and paste it. Now we're not. First thing we're gonna do is set do the is moves we're gonna do the is moves safe thing. That's the other bit of, boiler plating, I guess. Yeah. And to be honest with you, you don't I mean, so this is like the the default logic. Technically, you can implement logic that doesn't use this, but this is just like the most straightforward way to do it. So Sure. So this is avoid walls. So my head exists. It's movesafe.leftforce. Great. Board height. Interesting. I think we just called that We call it height Height. So we just need to change all of those heads to my head and all of those heights to No. Head to head is correct. My head. Oh, you just got to use my head? Okay. Yeah. But board height. So what we're gonna do here is say height. We'll put bring that in as board height so I don't have to rewrite everything. We'll bring this in as board width. There we go. Job done. I'm also just while we're here, I'm just gonna just gonna we'll make things just a little more little more succinct, perhaps. This is valid. Right? Just so it's 4 lines as opposed to so many so many lines. This is valid JavaScript. Really? Yeah. I hate indenting so much. I would But indentation doesn't matter. So we avoid we avoid the walls. Great. And then it's avoid bodies. And we've already got the snakes. We've already got snakes stored here. So that is the equivalent of that line there. And so we're gonna take this for loop. We will dump that in here. And once again, I'm just gonna take this moment to just, to just clean up a little bit. This time because the conditionals are longer, I probably am inclined to leave them to leave them like this, but just just remove a bunch of, you know, white spacing. And there's some funky indentation going on here. There, boom. There we go. All right. Then if there's any save moves left, the console log and we return move down. Oh, if if there are none left, we go down. I got it. I got it. I got it. So, all the safe moves if no go down. Great. There we go. And it won't be return. It'll be red stop return red stop JSON. And then it won't go further down. Alright. And it will have returned there, so it won't continue. So I don't need to announce or anything. We pick the next move. I am noticing hang on. Hang on. Game state dot turn is not correct. Let's make sure my head piece. Yep. Snakes. Everything there is fine. It's just it's just here. Game state doesn't exist. That's req.body.tan, which means oh, no. Req. Yeah. That's fine. Req.body.tan. And then res.json, then we'll look at safe moves.length, pick the next move. We'll log it. And we will return it. I'm sorry. It isn't a string. It's a it's an object, isn't it, with a move value? It is Next move. So I think I think that is that's it. That's that's a whole efficient, Kevin. That was wonderful. Okay. Thank you very much. Thank you very much. Yeah. I think that is our our snake as implemented last time. Let's rebuild the extension. Let's restart the. Once again, that shouldn't need to be the case, but it is just at this point we're recording, so so be it. And oh, that game in progressing has persisted to be a challenge. It has. So ignore. If I refresh, does it know it's done? No. Okay. So New game. We'll have to keep. New game, and we'll send in, I don't know, loopy bot. Or does loopy bot just do the Just does the loop. Yeah. Yeah. Hungry is good. Hungry is probably a good little test. Hungry is good. He was a good test for us last time. So I feel like we can see how he responds. Okay? Obviously, it's a little more verbose down here. You can reduce the amount of logs with an environment variable. We died that, you know, that's the end state. So, oh, wait. If it won't, can I hit play? Yeah. You can hit play. And sorry. Show the coordinates. Fast, please. Save game. No. Just go back. Just go back. No. Oh, do I just I'll back back back back back. Alright. So r one is the orange is the, oh, we won. So what did it say in the logs that you thought we lost? No. No. We we didn't lose. It ended. I sorry. I said we died, but I meant it ended. What does the what does the actual response say though? I'm not I'm curious. I don't have any idea. It is game ID rule set map stand up source. The oh, it's the it's an n the last 10. Yeah. Yeah. But does it say 1? Let's find out. I feel like it somewhere should say 1. I think no. I think snakes is just one long. Oh, yeah. But it could be you or anyone else, I suppose. Fair enough. It may not say it in there. I'm just curious. Maybe it's the fact you still exist. I mean, let's find out when we die. Do you know? Oh, yeah. If we look at you, it has the ID oh, let me, woah. There we go. I was gonna say I feel I felt like I wasn't seeing it, but I feel like Sorry. That was, that was a keyboard shortcut thing going on. Alright. It has a health. I wonder if the health knows that. I mean, we'll just do it a couple of times, but let's let's watch. Let's watch our our little sneaky there. You do it well. Hey. Like, that logic is good. Okay. Head to head combat, which we haven't built logic for. Maybe we'll do that this time. It's a good idea. I think we because I because I kind of feel like that was what we intended to do this whole episode. Yeah. Congratulations. We're done because we can't build it in flows, and that's the that's the thing here. I have one other I maybe you can execute a flow from Yeah. From this, and then this just becomes the entry point into the flow. That's what I was just gonna say. I was like, is there a way that we could set up the one of the endpoints in here and then have the trigger in flows? You're able to do everything from there? Maybe. That might be fun. I feel like that's an interesting way because I I do think that flows is like this interesting. I've never done it, but, I'm sure it will be fine. So let let's take another look at some new extensions documentation that was recently written. The resources on the not this one. I'm looking for resources, components, packages, extension services. Here we are. So underneath directives, every time you make a request, it's actually going off to one of these services, which then, you know, which then executes your your request. And inside of extensions, you can also access these services. Various services are available within directors, including item service, collection service, file service, and they're available within the extensions context. So there is a router here in the handler, but I think we can also pass in this context. Right? Let me just think about the best way to do this. I think it might be just playing around with this router dot get here. So, so we have this context. I'm gonna bump that back up actually. And if we console dot log, the context and we see just here, we, destructure get schema and services and then we destructure the individual service. So let's let's just do that outside of the individual. Outside of the individual. No. I think for just a moment, we'll do it just inside of this one just because this is the one we're gonna hit. So we don't we don't just have to mess with that. And then instead of doing this, let's just console log the the services and just see what happens. I'm actually gonna do a console dot here, services dex null. And what this will do is effectively a console log that never does this kind of cut off thing. It'll always show you all of the nested layers just because I'm not sure what it looks like and I don't wanna I'd rather just be a little more verbose once. So dotcom. So what we're gonna do here is go back to our root endpoint and hit enter. Oh, sorry. Not root. R snake. There we go. And what happened here? So we have some services. Let's, yeah, let's just expand this for a moment. Webhook service. I, there is a flow. I, I believe a flow service. Oh, nice. Gonna that's gonna wanna be the one that we I mean, is this the one we wanna I don't know. I mean, all we wanna do is hit all we wanna do is hit an endpoint. But Yeah. Let let's go for have a let's have let's have a play. Sorry. Go on. No. I was just gonna say, do we wanna go in? I don't know. Never mind. Let's let's test this out and then and then we'll go forward from there. Sounds good. So let so what you do here is you create a new service with the collection there. So you know what we'll do here. There are some commonly used services. We can just refer to the full list of services directly in the code base here, which is actually what I should have just done. So we have flows here. So we can create flows, create many, update, delete. This actually isn't what we want because this is all about running flows. Sorry. And the the crud of flows. And we don't wanna do that. We just wanna we just wanna run a flow. So maybe the first thing we sorry. Go ahead. No. No. No. Is it just the webhooks that we want, the webhooks service then? No. Because that's to create a webhook. That's not Yeah. Exactly. That's outbound. So so what we'll do here is we'll go to the admin. Oh, wow. We will create a little flow and we'll see if we can trigger that from our code. That's basically the test here that it works. So we create a flow. We'll call this info. Snake info. All of that feels good. Maybe traffic activity in logs. I think that's fine. Then we have the webhook. Webhook. You're right. It will be a get request, response body, data of last operation. Feed all the data just to have it? That that that's happened. So oh, no. So this is the response body. This is what it will respond with. We only want to we wanna create the response and send it. But, actually, what you're describing is it was here. It was this option here, track activity in the box. Alright. There we go. So back to the trigger setup, we'll turn cash off. We don't want it to be asynchronous, get request, save, And we will re- we- it will be the data from the last operation. So we'll just run a script and we will just return this, I think. I mean, that makes sense. Yeah. That works. I've not done this before. So we are we are really finding out together how this works. Works is very smooth. I like just being able to have it. I really like it too. So we've got a save. So now we have this idea of a flow, and I think that's the thing that's interesting to me at least. So you'd rather use the ID than actually just use the flow endpoint, like the webhook endpoint? I could. I don't think that's, like, the proper way of doing it. But could we do that? Yeah. I mean, I feel like I feel like Okay. I'm interested in seeing both ways, right? I'm interested in seeing ID flows, which I think is the proper way, and then just calling the webhook from within the code. Right? Yeah. I'm gonna be I I don't even know how to make a web request in these off the top of my head. Just I think it's just Axios, isn't it? I think it's just Axios. But but how is Axios, like, exposed? And do you wanna know what? I'm pretty confident there are just some guides that were written around this that I think will just help us. Endpoints create a public API proxy. Here we go. This will this will help. Yeah. It's a it's a fetch. Oh, alright. Cool. So we will so we hit slash. So what we're gonna do here, we're we're forget all these services for a minute. They don't I don't think they're gonna matter. No. They won't matter because literally we're just basically now that we've created that flow, we're just doing a call to that webhook and then Yeah. We're gonna do a response, await, fetch, and then slash flows, async. Okay. And then, yes, this is response okay, response Jason await Jason, all of this jazz. I normally, honestly, just do this pretty low rent, then r r dot JSON and our response is the data. So I think we can just return reds dot JSON response. Yeah? Yep. I might just be explicit here. Flow response. Flow response. Yeah. Yeah. And maybe yeah. That yeah. Screw it. Let's try it. Let's do it. Gotta get this I feel like this is why I enjoy, like, non typed languages because you just mess around and see what happens. Yeah. I mean, you can build extensions just as an FYI with TypeScript. I just didn't oh, yeah. I thought this might be a little easier. So if I hit refresh here, we should see the same the same thing the same thing. Oh, wait. Hang on. I don't know why that's happening. Oh, was there an error? You have triggered an unhandled rejection. Failed to pass URL. So it didn't like the Yes. So just add the link to the I mean, sure. But that that doesn't make this portable. Right? That isn't let me just have a quick think here. Oh, I see what you mean. You wanna be able to pat yes. Okay. I see what you mean. Because then if we deploy it somewhere else, this will Yeah. This will still work. That's that's that's gonna have another little look. Let's keep having a little look here. So still sort here. No. Because now I'm right because this is the flows. All I wanna do is know, yeah, how do I run a flow? Let's just do you wanna know I think I've just had I think I've just had an idea on how this works, which is actually not looking at the services. It's looking at the the way the SDK does it. So if we look at the, extensions, packages, sure. The SDK just like type, type doc. This is just the the full dump of everything the SDK can do. There is an endpoint in here called something there's something in here for running flows. Andrew, I'm going It is I love a c. So even if you write the documentation, everybody, you still need to read the documentation. You do still need to run it. Here it is. Right. Create a flow, retrieve a flow, list flow. So this is all the stuff we just saw, but Yes. Here we are flow with get webhook trigger, trigger flow. What is going on here? Let's take a little look. Failed to pass URL from /flow/trigger/uid. Yeah. That I mean, that makes sense to me because it's not a full URL. Right? Do you wanna know what I did sneakily on on the side here. I have a chat with one of my colleagues because, of course, I did because I have that power. It does just need to be the full URL. So let's stop. Let let's stop battling against I was right. You're right. You're right. But you're right. It's not portable. You're very true. It's like you'd now need to, like, worry about changing this if you change where you're you're hosting it. So I do think Which I think won't matter for the scope of this. So now built that, restarted that. So now if we hit this, it did return and it did it by getting by by getting the the, yeah. So, oh, that's really exciting. Okay. Let's abstract this logic out then. Let's get this whole move thing. Let's just comment it out. We know it works. Okay. And what we're gonna do here instead is we have a post. And so, let's set up a new flow. Not here. Let's set up a new flow. Oh, that's so exciting. Okay. Cool. Snake. So we're gonna we're gonna create a move we're gonna create literally recreate this entire logic as separate flows. Why not do it as one flow, though? Oh, because it No. No. No. Just because it won't be each endpoint. So everything move is gonna happen in one flow, and that that's yeah. Yeah. It's cool, isn't it? I think track activity and logs, I'm going to level with you. I'm not I'm going to say don't track anything because it's just gonna it's gonna absolutely explode the logs because it can be every like, the full body every time. And we have the the the logs over here. So we won't do that. That will be a webhook. Data of lost operation, that's fine. Not asynchronous. Don't cache. Post. Okay. So exciting. Okay. And we will, run a script and we will return move left. Right. We'll just we'll just do that to start and just test test it works. Return move. Hit save. And we have an ID for this one. Great. And, what we're gonna do here? We're gonna do we're gonna copy this. We are gonna change the ID and we're just gonna go post, sorry, methods. I wanna say it's post and I think it's body equals JSON dot stringifyreq.body. I think that's I think that's the fetch. I think that's the fetch way of doing it. So What is the fetch way of doing it? Is it good? Okay. Fetch. My memory has not failed me today. I'm literally I'm so pumped that we're able to do it in flows. That was a really good I I think that was yours. You can have that one. No. We're gonna I'm gonna give that that's that was a 5050. We left brain, right brain thing here. I feel like it was one of those days. Okay. Okay. Alright. So we're ready set. Ready set Battlesnake, Hungry Bot. Sure. And start game. What's happening? It's happening. It's happening. It's happening. It's happening. But it will just go left, left, left, left, and die. Yep. Right? That's fine. Yeah. That's fine. It's still it's still going into the world. Awesome. Which means we are done with the code. We're not done. We'll need it for reference. Yeah. But we are in flows now. It's happening. What's interesting here too, folks, is, like, you like, literally with those two endpoints, like, you don't actually need to implement start and end. Like, you can stop with just those 2 if you're like, I don't wanna have to manage 4 flows. Like, you don't actually need start and end at this point. And so I think, like, don't use them. So first thing we did was avoid walls. I'm gonna run a script here. We'll return it, and then I'm just gonna do the it's annoying. You have to kind of create it from an existing path. Yeah. And then we're gonna take that and dump it in there. Okay. Avoid walls. Back to the back to the primitives. Now, you know what we didn't do? We didn't because we don't have the logs. We actually don't quite know what it looks like in here, which is annoying. Just a thought I've had. Yeah. It's true. But I think I know. Well, I mean, let's see. Let's see what happens here. And I think the cool thing here is, like, now we return to, like, traditional programming as well. Right? There's a lot of different ways you can implement this within the flow. Like, there's so many different operations. Technically, you could feedback a lot of the information and then make, like, decisions there or just do it. Let's let's return the data. Like, that's the whole data chain. And then so this first time, this is gonna fail absolutely miserably. But, oh, no. It won't log anywhere, will it? Do we wanna turn on those logs then? I know we didn't want to, but Yeah. Yeah. Can I turn them on then turn them off? Yes. Fine. Yeah. That's fine. Yeah. Yeah. Yeah. Sorry. I thought it was a one and done kinda deal. So let's create a new game. Create rematch. Okay. Cool. Whatever. Sure. Refresh. And there should be many Yeah. 8. That was 8 moves. So it's it's gonna get unwieldy, but this is what matters. That's great. That's awesome. Yeah. So we get Except the distinct so the trigger where's where's all the, not being oh, so Are we not sending it? Are we not sending it precisely? And this is why I wanted to check. Yeah. Yeah. Oh, wait, what did that say failed up there? Go back up to the options. No, no, no, no, no. Force. Okay. Cache enabled force, cache enabled force. So cool. We have this fetch. We send that is how you that is how you do it. Right? I mean, I could have been wrong. I was assuming, but ah, here we go. I'm glad that it has happened. Yeah. Body.stringify. Body data type must match content type header, but I I don't think sure. Do you wanna layer what? Sure. Let's just whack that header in. Just in case it's that. Yeah. See what happens. I'm not losing any sleep over over adding that. Is that an error? Oh, no. We've left a log we've left a login somewhere. All the serve hang on. They're all the services. Yeah. Didn't we didn't we remove that? No. It's the it's the context. It's this console log context. Oh, there we go. I was like, that's a big that's a big object. So right. Restarting. Okay. Okay. Okay. We're gonna create rematch. Let's get some logs. Nice. Oh, head to head. Some quick a nice quick death. Okay. Are we getting Payload. There we go. There we go. There we go. So it's in trigger dot. Body. Body, I think. Yep. Yeah. Trigger dot body. Yeah. That looks correct. So, I mean, there's one way to find out, right, which is, so, trigger data dot trigger dot Bobby. Nice one. Bobby. So let's go and take this first up. So first thing we'll do, of course, sorry, is we will is move safe. We have to think about how we, because we, we need to kind of alter this value as we go. Maybe we return that. Maybe we return that with each step. Right? So we'll start with a is move safe and we'll just return it every time. So Okay. We'll start this time, we'll create it. I also think that could be a const. Anyway, avoid walls. So we need these values. Do you wanna bump up the text size there a little bit more? Thank you. You know what? I'm actually inclined. Let's rename this one. Let's call this one set up variables. Yeah. Like that. Let's call them let's call it globals. We'll just make the key global so it's a little shorter. All we're gonna do is return is return. We're gonna return what was it called? Is move safe. We will also do forward width. It's going to be data.trigger.body.width. Old height. What was the other thing? Oh, my head. So that's all of that. And then, we yeah. Just return that. Right? Yep. Sponsored global. Yeah. Yeah. We will. Just just I I wanna be I wanna be testing early in Austin even though, you know, we're not doing much there. I just wanna I just wanna be validating that we haven't broken something there. So we will create a rematch. So all we should be getting now is an extra property in the data chain. Wow. That was a really short, really short game. I think it was, like, 2 moves. Yeah. Oh, rec is not defined. I think I left in a a remnant of what it was before. Rec.body. Yeah. Data. Trigger dot body. That's why we test. Create a rematch. What else have you used Flows for in the past? So I did the, the DevCycle Directus integration that I built out. So this was, it's worth checking out, just to see, like, the different use cases here. So it basically allowed for you to do an API call to a patch API on the dev cycle, patch API endpoint on the dev cycle platform to, like, pass basic data into into the service and to, like, put, like, an edge database for feature flag management. We might use that actually, when we start to do the dev cycle integration just because it's a really fast way, and I know that it already works with Directus. So, nice. But yeah. That's called EdgeDB. EdgeDB. Yeah. So it's just, it's just, like, a faster way to pass data. So how are we doing here? No. No. But if I refresh, I think I fixed it. Yeah. Right. That's everything in the in the global. Now I'm not sure. So I think is move safe is one that we might need to pass, like every move we're going to need to we're going to need to reset that, but that's fine. So cool. Next thing we'll do then is avoid walls. Avoid walls. We're gonna run a script. We have data, and we have our avoid walls logic here. Yeah. I feel like this is also just a really nice visual way to represent this whole This those are the steps. I really wanna go and find my Node JS snake or my, my Node RED snake that I created because I did the same it was the same process that I went through. Was it sorry. Just very quickly. Well, did I call it my head still? I think I did. Yeah? Yep. You did. Yep. So the nice thing about this is we have is say is move safe left and all of this. So I think we wanna create a new variable called is move safe, and the value of that, I think this works, is gonna be data dot globals. Was it multiple? No. Global. Yeah. Global. Yeah. I think so. Yeah. Dot is move safe. Right? Yeah. This this is And then yeah. This is something that's really interesting to know having used flows before. I was not taking advantage of the globals like that or not the globals because that's the thing you're creating here, but, like, the But the data chain. Touch on data chain. I thought you lost the data, basically No. As you went through. So that's interesting. And I've had another thought about this. I think the way we're gonna do this is using last. So last, dollar sign last is a dynamic variable that will always be the output of the last step. But that means this line will become basically a boilerplate first line on every step in, I think. I think this creates a copy. We'll find out. We'll find out together. So next thing we're gonna do is we're going to destructure, my head board width, and board height from data dot globals. And then the rest of the code should be able to be untouched. Right? Exactly. Yeah. It looks good. The only and the only thing we wanna the only thing we wanna return is is move safe. Only thing we care about. Yeah. If this works, I'm gonna be so pleased with myself. I have a hunch it won't just because, of course, why would it? Let me just move return move out the way. In fact, we can remove that. So no. Of course not. Sorry. Ah, I've deleted it now. It's fine. We're here. So return move because what we're gonna do in this step is we're gonna just do that last bit of logic. If all there safe moves, if no go down. So we're gonna get rid of that. All we care about is next move and return. Yep. Because that is a a step between where we are and where and where we're going. So I have in my here we are. This is what we just had in our last step. So we have beta dot globals. Now is move safe. There we go. So next move, safe moves. Is it safe safe? Why is it called safe moves? Oh, here. I need this line too. I'm gonna call that one. Safe moves. Object dot keys is move safe. Filter down is move safe key. Fine. Fine. And that's just getting the the the values. Then next move, is safe moves, math floor safe moves. So that that should be it, And then we should just be able to return move next move like so. So that's the last step in. That's the last step in the chain. Let me bump that up one more time. Sorry. So that's all the logic again. We have that boilerplate starts. We don't quite know if that works yet, but in theory It should. Theory, it should. It should. Dangerous last words. Those. Sorry. So we'll just set ready set battlesnake in there because we're not equipped to have multisnake yet. Nope. Did not work. What are our logs saying? Thank you. Thank you very much. Yes. It did not work. You're welcome. I feel like I need to add these useful Last is not defined. Absolutely. I know exactly where the typo was. It's data dot last, and it will be wrong there as well. Data dot last. Right? Yep. Yeah. Cool. And what's nice now is you're not having to restart Directus every time because now it's handled by the flow. That's wonderful. It is cool. There was a little bit of of, let's call it friction first time. Okay. That's not ideal. 42, 44, payload. Cannot convert undefined or null type. Okay. What are we? Payload after after but here's the thing. After avoid walls, look at that. It did say up force, which means everything's working until the return move. So that's fine. So that means it works, this whole passing passing is move safe each step. Yeah. But where it's breaking is this last one. Okay. So let's look at this again. So async function the error was cannot let's actually read the error. The error says, cannot confer undefined or null to object, which means the issue is here in object dot keys. So we have is move move safe. I think it can just be data dot last if that's all we're passing step to step because we don't return an object with is move safe. We just return is move safe, which means here again, although I mean, let's see. Let's see what happens. Yeah. I think I think that'll fix it. So if we hit create rematch Yes. It avoided pause. Nice. Lovely stuff. And let's just, let's just validate that was indeed what we needed it to be. Left. Hey. There we go. Heck, yeah. Which means we're onto we're we're now onto a winner, basically. It's this is the starting logic for our for the no. It isn't. Oh, it's it's somewhere in between that we haven't quite got the standard because this is the first one after the global and this is the last one. So that's fine. We need to create one more here. Yep. And, what's next? Avoid bodies. Avoid others. Yep. Run script. Paste that in. And we don't need is move safe anymore. We have the data dot globals. And where are the where are where is the logic? It's here. And just because I think I think it's gonna be easier, I'm just gonna great. Great. And I don't think we need board width or board height anymore. We just need my head. We have snakes. So I believe we had bored. I think we got bored out. Yes. I'm not sure we did. Let's look at the globals again. The actual board itself needs to come with 2. So, let's call it snakes. Let's just add snakes here. So data dot trigger dot body dot board dot snakes. Then let's create this last one again, as avoid bodies and that should have saved, but it's fine. Okay. So we have my head and we have snakes. I think I gen oh, yeah. I genuinely think nothing needs touching here. No, I think it's good. And I think we've the, the code works and we're even past. Yeah. I don't see any point of weakness in this at the moment. So we hit say, oh no, we don't, we just, Famous last words. We do that. Then we do that. So I'm gonna just Very good. We'll hit save. Oh, what's going on? Value fulfilled resolving collection, direct us operations. Value fulfilled resolve. In fact, it has to be I, don't know what that is. Record not unique. For field, resolve and click direct operations. Maybe if we turn off that logging now. Just turn off the logging because I think that's Yeah. But that isn't logging. That's a database save error, but I don't know why. Let let's just forget this was here for a moment like it was before, and it was fine. Okay. That does save. So if I take this instead and send it to avoid bodies and hit save, it's fine. And then if I go here to return move, fine. Do you wanna know why I'm just gonna walk away slowly? That's the right that's the right answer. If the thing starts working So we don't wanna create a rematch now because we wanna create a game with another snake. Yeah. Yeah. Star hungry in there. Alright. Let's see how this works. Okay. We got it. He Hang on. We've not had to Yes. Hey. There we go. Yes. Nice. Get in. Okay. Done. Nice. So the final thing here was this, if there's no safe moves, go down. I don't think we need that. I genuinely it's like a Oh, yeah. Backup. It'll always Yeah. Well, because if there are no safe moves, you're toast anyway. Exactly. So it's really just, like, if you want to have, it I don't know. It's good programming practice for you to build it in, but, like, I think here, like, we can worry about that down the road. Do you wanna know what? Given that this was basically the scope for this episode, I'm gonna add it. Why don't we add 1 one new block in? Right. Because I wanna do avoid avoid head to head. K. Yep. That seems easy. Yeah. Because we're just basically doing look ahead here. Right? We're looking at, like, one extra block ahead. So so yeah. I mean, so this is the if you didn't catch it on the last stream or on the last, the last episode, we so head to heads, you can do and you can win, but it's all about size and rules, but, like, these head to heads here. But with the logic we currently have, we're only ever looking at what's directly next to us. So ensuring that, like, if we have another snake beside us or directly in front of us, we'll never go that direction. But if there's a snake that's 2 away in the same direction, so basically, we're repeating the logic that's in here and just or we could technically revise this logic. I think it's similar because it's just like Yeah. Yeah. It will be similar, but I don't I do not think it's the same. So it's not the same. If we ditch this for a moment, we'll leave it as we could see it. So we care about the snakes, every snake, and we're gonna get rid of us. Now there is a a fancy way of doing this, which is, js return array without first item. To remove the first element in the in the array, you can use shift. Shift removes the first element of the array and returns it. So I think we can do this just to get rid of us first. Yeah. And then we have to find our because then we would have no valid no valid way to go. So So now we care about every other snake. So I'm gonna say head, it's gonna be snake dot body. Does snake have a head too? Does every snake have a head? Everything has a head. Snake dot head. So okay. Cool. And what we wanna do is look one space left, right, up, or down and remove if that's gonna be the direction we go in. Right? But we I'm just trying to have a think here because I think these are actually much more complex if statements. They are. It's basically if I remember having done this before, it's basically all of those if statements that we had there repeated 4 times, if that makes sense. Which we absolutely will not do. We will we will do, like, a nested loop. But I'm just trying to think through that. I'm just trying to think through, like, what is the logic here? Well, the first thing we wanna do is check every feasible space we can move into. That's the way we're gonna do it. So feasible directions. I remember encoding Valdez to do stuff like that, and then we had to remember what it meant. Possible next steps. And we actually don't wanna do this inside of this loop of every snake. We wanna do this just for us. So possible next steps are, is I don't know that because that's gonna be possible next steps is an array is an array. Cool. And we're gonna push into this array everything up, down, left, or right of us. Yeah. Then we're gonna remove our body piece, our neck. We're gonna remove our neck. Yeah. Okay. Cause we don't want our neck in there. So that, that will release 3 directions. And then for every snake, we're gonna repeat that logic. We're basically gonna say if any of these match, you remove it. So when we store them in here, we also have to store whether it's up, down, left, or right. K. Alright. Let's see. You have a plan, and I'm very curious to see how this will be implemented. First thing we wanna do basically is say up, down, left, or right. What is the coordinate? So what are the coordinates of that? So we're gonna say, in fact, I I think this might be easier. So we're gonna go possible next steps dot push. We'll we'll start with left. No. Left is going to be x is, come on, Kevin, you can do this. X is going to be our current X position. So this is going to be I don't just want my head. I want I want you. So I'm gonna save this. We'll come back here. I don't think we have you. No. No. We have my hand, which is great. Let's just throw you in there. Then we'll come to avoid head to head and what just while I'm here. And return move. Right. Return head to head. So now we have access to you in here. So we want Datastock Global's my head, you, and my head. Cool. So that's gonna be you dot, why why did I do you? We just care about my head. What are you doing, Kevin? Yo. Because you wanted to remove the neck. You were this Yeah. But not oh, yes. I suppose that will be that will become important, but the neck comes later. Yes. So we just wanna say my head dot x minus 1 and y equals my head dot y y. That's left. Bright. So that's the the same. Up. And so that's gonna be x remains the same, but y this time, r is minus 1 and down, plus 1. No. It starts the other way around. Down because bottom left is 0. Up. So that's pushing in push in all I mean, it's it's pretty obvious actually by the, by by the name of what's going on. Then we wanna remove our neck from that. Then there'll only be 3 items in the array. So we'll go possible next steps. How do I do this? Equals poss that becomes a let. Possible next step, stop filter. Possible next steps. So steps, where the step is not equal to you. You dot what is the it's a bit disruptive, but I'm gonna just what is the what's it called? U.body.body. I love that my my many, many if statements, my heuristic was not okay, but there's this will be so much cleaner though when it's done. I will agree with you there. Thank you. So this is remove net. I think that's what that does. So every item where that because all the body parts are is this. It's an x and y. Right? Yep. Yep. I I'm a little bit I think I think object, object comparison is a little bit hunky. So what I'm gonna do just to be on the safe side here is say, where s dot where s dot x is not equal to x and where it's not equal to y. So if both those things, no or no and hang on. This is this is important. I'm gonna do it the other way around. So where they are equal to x and they are equal to y, that means, hey, this item matches or wrap the whole thing in another bracket and we will negate it. Okay. That should be removing. Does that make sense? I'm so glad we have a video because that's super unreadable, but I get the logic makes sense. No. No. No. Not not unreadable. Unreadable. I mean, like, I feel like if somebody looked at this, they would be like, what is happening in this line? So, I mean, I mean, we're removing the yeah. Yeah. Right. Okay. Sure. Yeah. So Who needs to read this code except for us? Exactly. No. This makes sense. Okay. Yeah. I think that works. So we removed the neck. So that's removing that's saying all of our possible neck positions. Now we wanna basically figure out all of the every other snake's possible next steps. And if there's any matching item oh, there's a there's a silly, silly little thing going on here, which is we're gonna push in. I'm gonna just call the variable c to mean coordinates and that and this is where it all starts falling apart in terms of, like, clarity. And I am gonna I am I I've decided I am gonna be more explicit here. No. I think it was I mean, I think it's I think it's very clean code before, but, yeah, I think that this makes a little bit more sense. Yeah. So we have the cohorts. And the reason this matters, It's because we don't just want to store the coordinates. We also wanna store the actual reference to which direction it is. So as well as the quotes, we are gonna say direction left, and we can get rid of that. We can get rid of that. So direction left, direction right, direction down and direction up. Right? So now we just have to change this bit here because it's not s dot x anymore. It's s dot quartz x and that's dot quartz y. But it will when it removes it, will physically the directions will no longer and then at the end, we're gonna just return the those directions. So I think that that's an important thing. So we can actually ultimately at the end change is move safe and know what values we are we are removing. Yeah. Because before, there's no how do we know which one's which other than the order we initially push them in? Mhmm. Not a nice. The next thing we want to do is go around every snake, and we wanna basically do this same game here where we go left, right, up, down, push it into an array, cross reference them. If any of them are the same, we know the direction in which they match. Oh, that That's funny. Off reactions gone. Are you familiar with these? I am, but you're in Arc. Right? Is that where it actually is? Or is it within Riverside? No. It's it's macOS. Oh, it's in Mac. Really? No way. Yeah. But I don't Well, I'm not I'm on Windows. Windows is like, no. We don't we don't think these are things that we need. That is so funny. So now we now we wanna do the same thing for this snake. And this is this is the important thing is that for every snake, we are literally gonna do this exact same logic, except we're not gonna remove the neck. We're just gonna say, oh, are we gonna remove the neck? No. No. We'll just remove the head. No. No. No. No. Because if we you know, because if we're colliding into the neck, we're colliding like that would have been handed by the dome. Yep. Yeah. Exactly. Yeah. So it's just the head. Exactly. Exactly. So we just care about this logic. So possible next steps. And I am just gonna just shrink this down slightly because it'll be contextually clear what's going on here. So it's no longer my head. It's no longer my head. It's head. Head. Okay. And so that's so that's that. We're doing that for every single snake. And now this is the important thing we are going to compare. Yeah. We are going to compare. Let's just have a think about this. We actually don't care about the directions this time. The directions are irrelevant. I think, I think we'll leave them in for the sake of having the same data structure. So we know exactly what we're doing, but this bit's irrelevant. We just yeah. It's it's irrelevant. So this n is going to go before it's just one big array of every snake. We don't need to have it on a per per snake basis. So this is us populating and with every viable direction that could ever be in. Actually, I've had a change of heart. We are we are gonna Sure. That down. And then we're just gonna be comparing our end versus our possible next steps. Hence, look, we're just gonna be You absolutely you absolutely got it. I like it. Okay. This is cleaner than I thought. I was definitely you were, I get where your head is at with removing some of this stuff now. I love it. It took it took you getting to the end of this for me to be like, yeah. I I this makes sense. Good. Good. Good. Thank you very much. Thank you, Ro. Smart code. Very much. So now what we're gonna do is compare every single value in n. So we're so what we're gonna do now is and yeah. Yeah. Perfect. So we'll we'll loop now. So it's a for let n of, can't do n. Yeah. If you just change n to to next moves or something like that or or yeah. Yeah. There you go. Sure. Oh, I see. So for so next, I'd rather that's fine. For Yep. So I can use the shorter version inside the list. I'm about to use it, I think, a reasonable amount. So for every single value in there, is it one of those? It's all we care about. Let's have a think let's have a quick think about how we're gonna achieve that. Okay. So for every single one, we're gonna say, does next possible Does value in one array appear in another? Hey. There we go. Nice little vanilla JavaScript there. Absolutely. Include some and include. Some text element against the test function that return. Yeah. It's great. Cool. I don't think this is, oh, but it's that's no. It's that same thing again with objects. So I think the object nature of it, like, object comparisons are are specific. Yeah. It's a find and a size I think this is actually flipped. What are the responses? Did they say it actually works? I think so. That's a bit of a it's a bit of a big big example. Got it. Got it. Lovely. That's the answer. So I'm gonna just I'm gonna just pop it here and comment it out so we just have a little reference at the point where we're writing it. So this is an array. Let's consider this as our possible next steps. Yeah. So we wanna say, for, matches r.sum. It's not called r, is it? It's called next possible steps. Possible next steps. Possible next steps. Possible. I'll fix that in a moment. So sorry. The sum method of an array test whether at least one element in the array passes the test implemented. Okay. So next possible did I make a typo? Yep. Possible next steps, not next possible steps. Just Thank you. It's what I'm here for Possible next steps. And it immediately changes color to be like, yeah, you can spell now. Thanks. Possible next steps dot sum item. We'll do that again, and it's item dotcoords dotcoords. Dotxmatchn dotx. Dotxanditem.coords.y. For n of next. Is this is this how is this really I I think I I think there's some redundancy in here, actually. Yeah. What? Yeah. This is all we're this is this is trying to, like but we're already in a loop. We're already in a loop. So all we wanna know was, I've got it. Matching collision. Collision. Is that how you spell it? Collision Yep. Equals possible I got it. I got it. I got it. I got it. Next steps dot find and because we just wanna find does does one match. Right? And what we wanna know is step in there, is there a value where the cohorts dot x are the same as n.xandstep.cowards.y equals m.y. If this returns a value It works. Then that yeah. Then that collision it's gonna be a little bit of a long winded one to test because also we're gonna have to go we're gonna have to go back through the logs. This one's gonna actually be really painful to test because in theory, if it works, you know, but, so what this will return is a possible next step. And so what we will then do is if this this will have the value of possible next step in it. Mhmm. If it matches, so what we will then do is say if collision, because it will be 4 to undefined, like, some faulty value if if there's nothing returned in there, then possible just get myself a few characters because I think I'm gonna go quite far to the right here. Possible next steps equals possible next steps dot filter, and we wanna remove this item. And it's this. It's this logic right here that we already wrote, but slightly different. So we wanna say we're s.coawards.x, where collision by collision.cohorts.x. Oops. This one here, where it is the same as collision collision. Am I spelling it right? I I swear I swear I swear I That's right. No. You're good. You're good. I'm literally just like, you know, when words start to look wrong. Yep. No. But it's catching it. It's catching it, turning it purple if you're right. And this is the same as collision dotcoorg.y. Okay. I think I think this is it. Then oh, no. There's one more step. So now possible net, possible next steps is a shortened array which has just items which are viable. Yep. Just items which are viable. Let's just have a think because we we don't wanna What is that actually gonna look like, though? Do we know right now? It it will be an array of objects that look like this. Is it just gonna be a same case? Yeah. Yeah. But just a shortened a shortened list. Yeah. Now what we actually wanna do now I think about it is we kind of want the others, which I've just realized we want we want the other items. We want the ones that are being removed. So Oh, to So removed from his move. Yes. Yes. Yes. Yes. So let make false. This is we're starting to get a little hunky now in terms of, like, code quality, but, like, it's fine. So if collision we don't wanna screw around with possible next steps. We don't care about that. All we're gonna do is add collision dot direction to make force, and we're not even gonna do that. We are gonna just straight up say is move safe dot left, and the value of left will be in collision dot the equals false. And that's the direct we are making the the value that we came in with. Yeah. We're setting it to false because we're gonna collide. And I'm not sure if this is valid, syntax. I will find out. We will find out. I might I might just save ourselves some effort here. Commission dot the okay. Alright. Sorry for bringing oh, don't don't play this game. I, I'm sorry. I felt like that wasn't an overly, like, collaborative exercise, and I'm sorry. That was it was wonderfully collab I don't know what you're talking about. See, my favorite collaborative experiences are the ones where I can nod along and say yes, And I can I can help pick out when you've misspelled something? That's, I feel like I've I've contributed in my own YouTube. I don't know don't know what that little error is at times. Anyway It's weird. Let's and this one is gonna be a little bit of a hard one to to test. So we're gonna just have to watch watch this carefully. The snake's going up the whole time. Okay. So there's a bug somewhere. There's a bug somewhere, but we will be able to See debug it. Oh, it all died here. Un un there's a problem in the setup of the globals. There's a comma. Change in here. Comma. Comma. Those commas. It's always the commas. Grammar's out to you. I'm loving I'm loving now that we don't have to touch the code editor at all. Yeah. Agreed. Okay. See. So where K. Avoid head to head. Fine. Unexpected token line 8. Great. Okay. 8. There is an extra bracket here and an extra bracket. No. No. No. The one at the end is the one at the end is correct. There's an extra bracket here too. Great. Let's see if that does it. Yeah. I I literally cannot believe we're okay. I was gonna say we're getting it to work. We're getting things are happening. We're debugging, and that's the important thing. Alright. Snakes is not defined. Okay. Not a problem. What's the I mean, this is good. Oh, snakes. Fine. It's all really small, like, little errors, quite well defined, quite quick to fix, allegedly. Alright. Alright. Alright. I need everyone to know at the time of recording, it is 11:30 at night. Snakes dot shift is not a function or its return valuable value is not iterable. Right. That's that's fair. And that is here, snake dot shift. So what we're gonna do is just it really doesn't matter. Like, snake star shift, and I I'm assuming it that's how it like, it needs to happen on its own line first. Kevin, if this is what you programming at 11:30 at night does, I don't wanna see you programming when you're fully rested and awake. Well, here's here's the joke. I'm I'm never fully rested. I mean, children. Children. Yeah. Fair enough. Well, it's going it's going up, but I also didn't set that up correctly. But let's let's keep having a look at the logs. S is not iterable. Okay. I think I've misunderstood how shift works. 1st element. Oh, it returns the first element. I don't want that. I I want to return them. Right. Right. Right. It returns just the element. It pops out of the top, but I want everything else. Oh, everything but. Yeah. So what's the option of the shift? It is to do the head, avoid head to head. So it is to do the work. So it's to do the shift snake stop shift, and then I think it might actually be to put snakes back in there. Yep. But I'm not sure if snakes is re I'm not sure if that will work. I think it will. Okay. I think it's not doing that anymore. This is obviously not the test, the, we need to But we're not we're we've got no actual errors in the code that we've well, that we I use we very liberally. The code that has been produced in that final card is working and it's definitely a little bit slower now. So I think that we're definitely getting a, there's a lot more compute going into this, which totally makes sense. Yes. Okay. We're we're still going. Woah. That was a okay. Okay. It did end up giving up. Why? Oh, because there was no viable choice. Yep. So that's Which is yeah. Wow. Hey. We wanna move fast oh, sorry. What do you mean? I was gonna say if we wanna move faster for the head to heads, put multiple snakes in more than just one. Okay. I I think we I think we got a snake. I think we did too. I think it's working. I'll stick 2 hungry bots in that There we go. Cough. That should do it. Should have hit start game first, so it did its job. Okay. Head to heads are obviously gonna be hard to to start. Yeah. But we can kind of see we can kind of see here when it ends it ended. Okay. So now we can watch the whole thing. Yep. So he's not what's he turn why is he turning? Was there a safe move there? There was a safe move. How do I go back? Pause back. No. There wasn't. Nope. There wasn't. It got itself into a corner. Fine. I'm confident I mean, I'm not confident enough. I do that were there any head to head opportunities? And I didn't see one, did you? Nope. No. But I think I think we're not getting any errors at that final stage, and I think that's a good sign that the logic in there makes sense. You yeah. Yeah. But it might not be doing what we want. So what other few do you think 11 by 11 is still a good size, or maybe I should make it small? Make it small, and then we can see faster. And, yeah, give that a try. Yeah. Yeah. There we go. Yeah. Run that and see what happens. Oh, it's it it went on for a reasonable length of time. K. So there looks like No. Our snake didn't didn't need to didn't have a head to head moment. Well, I mean, it avoided a potential head to head moment there, but But it's I'm happy I'm happy in no. Yeah. Because we'll never know, will we? Yeah. We'll never know. We'll know if we play enough games over a long enough period of time, but I think the code is there. And we would know it doesn't work if there is a head to head, but they don't happen often enough. Hey. We leveled up our snake. That's solid. So not only move it to a different platform where it's running, same logic, and then adding that extra step and super visual. Super visual. I'm very I'm very, very satisfied. I'm very satisfied by it that working. Awesome. I think that brings us to time. I mean, we we have to be at time at this time. You gotta go to sleep. I've got yeah. My kids will be up in 5 minutes. So, awesome. Thank you for for for for doing this again. Yeah. Thanks for joining us, everybody. I feel like I'm glad that you were driving this time because had I been driving, this would have not gone as smoothly in any way, shape, or form. So I feel like this was gonna be be silly. But I I am excited to eventually drive when we, again, in episode 3 or 4 when we start to implement some other technology. So do we know what we're doing on our next episode yet, or is it all In our next episode, we're gonna stay in Directus, and we're not gonna be leveling up our snake. But given that Directus is backed by a database, we're gonna start actually storing data about games and then seeing what we might be able to learn with Directus Insights. I love it. I love it. That's gonna be an awesome next episode. So, we've got a snake. You've seen how it works. That's all of the steps for the starter snake that are worked out in there. It's actually just a functional JavaScript snake. You could pull all of that logic out. Well, most of that logic out and just put it into a JavaScript Express server and just go to town. But, but we've got it working in here. And so now let's do a little bit more. Let's see how powerful you can make Directus with Battlesync data. I'm very excited. Yeah. This is awesome. So until next time, this has been ready, set, battles, stake. We're getting better. We're getting better. Some some would say. Some would say. Till next time, folks. Bye, buddy.",[181,182],"79065ff8-a276-4134-a90c-04775dccb561","82d75a59-34de-4a81-8d98-59f44f95b638",[],{"id":134,"number":135,"show":122,"year":136,"episodes":185},[138,139,140,141],{"id":140,"slug":187,"vimeo_id":188,"description":189,"tile":190,"length":191,"resources":8,"people":192,"episode_number":195,"published":196,"title":197,"video_transcript_html":198,"video_transcript_text":199,"content":8,"seo":8,"status":130,"episode_people":200,"recommendations":203,"season":204},"visualize-battlesnake","925465713","Having build a very powerful snake, Kevin and Andrew seek to bask in the glory of their intellect by visualizing their wins. But not is all as it seems...","a1039337-33b5-4299-a982-bc258e3b8fa3",130,[193,194],{"name":151,"url":152},{"name":154,"url":155},3,"2024-04-18","Visualize","\u003Cp>Speaker 0: Hello? Oh, were were you waiting to do the thing? Alright. We can\u003C/p>\u003Cp>Speaker 1: I got you excited? I got you excited.\u003C/p>\u003Cp>Speaker 0: Hello, and welcome to ready Ready.\u003C/p>\u003Cp>Speaker 1: Set. Battlesnake.\u003C/p>\u003Cp>Speaker 0: We are running out of time to nail it, and I I have to be honest. My my, my confidence is starting to wane that we're gonna get it. But hello, everyone. Welcome to episode 3 of 4 for Ready, Set, Battlesnake. My name is Kevin.\u003C/p>\u003Cp>I work at Directus and\u003C/p>\u003Cp>Speaker 1: My name is Andrew, and I work at Devcycle.\u003C/p>\u003Cp>Speaker 0: Absolutely. And today, we're gonna pick up right where we left off in building a Battlesnake with, Directus and in the next episode with Devcycle. We've both been involved in the Battlesnake project for some time in various ilks. And honestly, we're we're just having a great time, if I may if I may talk for us both.\u003C/p>\u003Cp>Speaker 1: I'm with you. I think this has just been so fun. I haven't dug into Battlesnake in such a long time that this was such an awesome thing to come back into, especially oh, look at that. My light turned off. Especially after, especially after just getting into the the nitty gritty of Directus over the past, like, couple months, getting ready to build some of this stuff out.\u003C/p>\u003Cp>So, yeah, this has just been it's been so much fun, and I'm excited that we're only halfway through right now. Like, we've got 2 full sessions left, and I think it's\u003C/p>\u003Cp>Speaker 0: gonna be amazing. So am I. So let's recap where we were at just at a high level. So we have a local directors instance running. Inside of that instance, we have a custom endpoint extension.\u003C/p>\u003Cp>We have a custom endpoint extension because we need to know with confidence that, a snake can exist having a root endpoint and a slash move endpoint at the very least. And the webhook, the webhook triggers for flows can't guarantee that you get a random URL and we need we need both of those relative past. So we created an endpoint and all the endpoint does is it fires off this flow. There's one for the info of our snake here, which gives the information about our snake. Is the font size okay?\u003C/p>\u003Cp>I can bump it up if necessary.\u003C/p>\u003Cp>Speaker 1: It's okay. I mean, bump it up a little bit. I feel like it's not bad for folks that are watching. Yeah. Yeah.\u003C/p>\u003Cp>It's good.\u003C/p>\u003Cp>Speaker 0: Yeah. Yeah. Yeah. Yeah. And then our move endpoint, which is what we spent most of our time working on in the last episode.\u003C/p>\u003Cp>So request comes in with a full board state. We set up some global information from that state. We grab is move safe, up, down, left, right, or true. We grab snakes, board width, board height, my head, and you kind of all of these conveniences. Then we run avoid walls, and we see whether whether the wall's gonna be hit.\u003C/p>\u003Cp>But what you'll notice is at the top is that we pull in is move safe. We do all the work to it and then all we do is return is move safe. So we're moving this object down the chain kind of manipulating it at each step because that's ultimately all we need. We avoid the walls. We avoid all bodies of all snakes once again, starting with is move safe, ending with is move safe.\u003C/p>\u003Cp>Then we avoid head to head. This is what we spent most of last session doing once we got the flow running. It might not be the most elegant, but what we do is we look one space around every snake that isn't us, and we avoid those spaces too. Once again, we start with this move safe coming in, and we end with this move safe going out. And finally I love we determine\u003C/p>\u003Cp>Speaker 1: I love that you're saying this is not elegant, Kevin. Like, this is like yours is so much more elegant than mine. As you're gonna find out next week when I jump back in, week 1 week 4 are Andrew, learn learn with Andrew. Specials.\u003C/p>\u003Cp>Speaker 0: Andrew specials.\u003C/p>\u003Cp>Speaker 1: Exactly. So this is such clean code. I love it.\u003C/p>\u003Cp>Speaker 0: Oh, well, thank you very much. I'm sure some of our viewers will disagree and I won't stop them. And then and then finally, we grab that array of is move safe. We basically just pick 1 at random that is still available and we return that as an object move next move, which is either up, down, left or right, and we continue on. And so in this episode, I think we were going to start playing around with not necessarily adding more logic to the snake.\u003C/p>\u003Cp>I think it's a pretty robust snake right now, but instead, we're gonna start saving game data to this director's project, and then perhaps we'll do something with the insights module. How does that sound?\u003C/p>\u003Cp>Speaker 1: That sounds fantastic, Kevin. That sounds like a great way to spend an afternoon for me slash Wonderful.\u003C/p>\u003Cp>Speaker 0: And afternoon, no?\u003C/p>\u003Cp>Speaker 1: It is now but I realized that people are watching this from everywhere. So maybe it's from morning. Everywhere. Technically, it's it's, we've got afternoon for me, evening for Kevin, and morning for one of our wonderful viewers out. I'm sure somebody\u003C/p>\u003Cp>Speaker 0: is more important for you. Good good morning from 9:30 at night. Okay. So so so I've already had to think about how how this should probably shape up, which is this return move this return move operation here does some work, right? It picks the move and then it returns it.\u003C/p>\u003Cp>I think we should decouple these. So we should do one final step which picks a move and then the final step which just returns the move. And then we can put in like an inter we could put in one additional operation in which will store data in a database. I think that's probably the, you know, we have to decouple it and then we can we can take in the entire board state from the trigger, store that and we can store the move that we ended up concluding before we send it on. That's that's what I think we should probably do.\u003C/p>\u003Cp>There is a question of what we want to store. You know, we also have this additional start and end move point. I think end always returns the final state of the board. So there's something to be to be said about that as well. Yeah.\u003C/p>\u003Cp>What do we what do we want to store? Maybe we start with we we just store the the move. Like, we start really simple. We just say what is the final move we chose?\u003C/p>\u003Cp>Speaker 1: Yeah. I think that's probably easy number 1. I was also gonna say we could record win or loss, like, if we wanna go really simple. But technically, I think move is probably simpler because then otherwise, we're gonna have to parse through the JSON data that we get back. So yeah.\u003C/p>\u003Cp>Let's do let's do move and then maybe, like status of the game and, and then go from there.\u003C/p>\u003Cp>Speaker 0: Interesting. Let's just let's just go till we can't go anymore. Now just as a note, there is a limitation that we have to work within with directors. Now we actually just did a different directors TV episode on this. It was request review, filtering JSON objects, I think it was called.\u003C/p>\u003Cp>But, basically, if we store a JSON blob inside of a field in directors, we can't filter inside of that blob. It is just a string. So what we'll need to do is extract specific values and store those as individual values if we want to use them for insights. We just have to be aware of that. Let's let's start by doing what we said we would do, which is adding this extra, splitting this out into 2, and then we can figure out how we're gonna store data.\u003C/p>\u003Cp>I think that's probably the safest bet. So, let's add a new operation here. Let's call this 1, determine move. I think that feels okay. I think we'll give it the key of move.\u003C/p>\u003Cp>And I don't know why I've started doing that without having copied out this. So determine move, and this will be a run script. You can copy and paste that in here, except all we're gonna return now is move next. I think all we need to return is the strength next move. That's all this one has to do.\u003C/p>\u003Cp>And then we're just gonna re kind of configure these. So this one's gonna go here. This one's gonna go here and we hit save. And this keeps happening. Don't know why, but it seems to work if you just kind of what was it?\u003C/p>\u003Cp>Do it again? Yeah. Because that's what it was.\u003C/p>\u003Cp>Speaker 1: Disconnect it and then save and it should work. Yeah.\u003C/p>\u003Cp>Speaker 0: Like this. We do. Well, we did that. Okay. Cool.\u003C/p>\u003Cp>Cool. Cool. Cool.\u003C/p>\u003Cp>Speaker 1: Yes. And now we're\u003C/p>\u003Cp>Speaker 0: And then, which feels odd. Now today, there was actually a release of Directus the day that we are recording this, but I'm not feeling brave enough just just in case. Just saying it. So this this may well have been fixed. Do we remember what it was?\u003C/p>\u003Cp>Because it ain't ain't Don't say these. Bloody words.\u003C/p>\u003Cp>Speaker 1: Is it not working? See. Hang on. Hang on. Okay.\u003C/p>\u003Cp>What if\u003C/p>\u003Cp>Speaker 0: we go this way?\u003C/p>\u003Cp>Speaker 1: Yeah. Okay. Fine. And now switch it.\u003C/p>\u003Cp>Speaker 0: I think we had to, like, maybe.\u003C/p>\u003Cp>Speaker 1: No. What the heck? This this resolved with this last time. So this is\u003C/p>\u003Cp>Speaker 0: Yeah. But nothing nothing's changed, Andrew. Nothing's changed.\u003C/p>\u003Cp>Speaker 1: Okay. Go back, and, like, remove it from there, save, and then go back, go out, and come back in.\u003C/p>\u003Cp>Speaker 0: You think?\u003C/p>\u003Cp>Speaker 1: I don't think that's what we did, but I wonder if\u003C/p>\u003Cp>Speaker 0: it will No. No. We we definitely didn't do that. We did this and then this and then\u003C/p>\u003Cp>Speaker 1: This is why you should always watch your VOD recording if you're trying to bug\u003C/p>\u003Cp>Speaker 0: some of the buttons. We fixed it. Watch your VOD recording. We did. Oh, no.\u003C/p>\u003Cp>Speaker 1: Okay. Remove the okay. But so let's maybe there is actually an error in there. Maybe there is a new error that we've introduced.\u003C/p>\u003Cp>Speaker 0: Alright. But not nothing new has happened. Right. Value fulfilled resolve in collection, direct to this operation has to be unique.\u003C/p>\u003Cp>Speaker 1: Are move the same? Is determine move and return move? Is it the same key? Is that what it is?\u003C/p>\u003Cp>Speaker 0: Oh, possibly, but I really don't think so. This is no. This is called return move.\u003C/p>\u003Cp>Speaker 1: Nope. Nope. It's a return move. You're right.\u003C/p>\u003Cp>Speaker 0: So it works that way around. Cool. Cool. Cool. Cool.\u003C/p>\u003Cp>Cool. So so then we do this. And it does It's this. This is this is where there's a problem.\u003C/p>\u003Cp>Speaker 1: I feel like our logic is not going to work. Can we just trigger another what if we just trigger another flow instead of doing it past the data? I know it's not as clean but, like, if we're gonna, like, bug solve even just temporarily, why don't we just send it to another flow? It's a new thing we haven't looked at as well because you can trigger\u003C/p>\u003Cp>Speaker 0: another flow. I don't wanna do that. I don't I I want it to work.\u003C/p>\u003Cp>Speaker 1: I mean, we all want these things to work.\u003C/p>\u003Cp>Speaker 0: Okay. We're gonna pause and and we're going to come back when we have worked it out.\u003C/p>\u003Cp>Speaker 1: What was so wait, what was, what was the you're right. The solution was exactly what you described last time. Are you getting error logs in your console that are different than that?\u003C/p>\u003Cp>Speaker 0: Oh, no. Really? Shit. It's a great way to start a bloody director's TV thing, isn't it?\u003C/p>\u003Cp>Speaker 1: Fantastic. Bugs are what make this feel interesting. K. Oh, what?\u003C/p>\u003Cp>Speaker 0: No. No. No. This this one was the good one.\u003C/p>\u003Cp>Speaker 1: Oh, that's yes. That's the reversed way, so it won't work that way.\u003C/p>\u003Cp>Speaker 0: What have I just done? So this goes into return move. No. We want this one to go into determine move. Determine move goes into return move, save error.\u003C/p>\u003Cp>Speaker 1: An error. Delete that card and create a new one and see if it solves the problem.\u003C/p>\u003Cp>Speaker 0: Call return move. Card. Oh, doesn't like that.\u003C/p>\u003Cp>Speaker 1: Yeah. Just return. There we go. Same thing. Director's operation.\u003C/p>\u003Cp>What is okay. Can we see that that field go into the data model? Can we not see that operations logic?\u003C/p>\u003Cp>Speaker 0: I mean, I mean, yeah, I guess. But, like, we're looking at quite, like, low level, like, built in system. Okay. Cool. There is an error.\u003C/p>\u003Cp>Copy to action. This is what Riker's just messaged. Hang on. Did it work? No.\u003C/p>\u003Cp>Okay. I'm telling him what happened. Right. I meant I'm talking to the lead maintainer. Node 13 were.\u003C/p>\u003Cp>I made node 2 then changed the nodules.\u003C/p>\u003Cp>Speaker 1: And\u003C/p>\u003Cp>Speaker 0: okay. Fuck. Fuck.\u003C/p>\u003Cp>Speaker 1: This is the way to this is definitely what what what was meant to happen this week. Is it okay. What if we run a simple script? Like, just detach that new one that you just created. Add any random script after the determined move.\u003C/p>\u003Cp>Okay. So we need to create\u003C/p>\u003Cp>Speaker 0: a new one first of all. Right? Yes.\u003C/p>\u003Cp>Speaker 1: Yes. It's\u003C/p>\u003Cp>Speaker 0: great. Do will return data. Great. Right? It's like a pass through.\u003C/p>\u003Cp>It does nothing.\u003C/p>\u003Cp>Speaker 1: Is it the script? What if we do the same thing with the I Yeah. If we duplicate this script, can you duplicate a flow?\u003C/p>\u003Cp>Speaker 0: Not a whole flow. Oh, yeah. Flow? Yeah.\u003C/p>\u003Cp>Speaker 1: I wonder if duplicating the flow might solve this issue.\u003C/p>\u003Cp>Speaker 0: Duplicating the flow will mean we have to reconfigure the extension. But\u003C/p>\u003Cp>Speaker 1: Well, I mean, let's let's see let's see if it works.\u003C/p>\u003Cp>Speaker 0: Well, yeah. Well, I don't know why I'm doing that. So return. Okay. And we'll copy out.\u003C/p>\u003Cp>Blows. And then we can\u003C/p>\u003Cp>Speaker 1: Oh, no. I actually can't duplicate 1.\u003C/p>\u003Cp>Speaker 0: You could, once upon a time. No. It's insights. That's insights.\u003C/p>\u003Cp>Speaker 1: Good to know. Fucking Here we are. Now we'll figure this out. We got this. I have I have faith in us.\u003C/p>\u003Cp>Okay. So our regulars our regular script\u003C/p>\u003Cp>Speaker 0: our no.\u003C/p>\u003Cp>Speaker 1: Our regular script was working. Okay. Skip just to the end. So, like, remove the the head to head and go straight from avoid body to the very end. So skip over the avoid head to head because this is where we started to get these issues.\u003C/p>\u003Cp>No. It's the final card that's fucking this up. No matter what it is.\u003C/p>\u003Cp>Speaker 0: It's reassign.\u003C/p>\u003Cp>Speaker 1: It worked. Did it work?\u003C/p>\u003Cp>Speaker 0: It worked. Don't question it. Okay.\u003C/p>\u003Cp>Speaker 1: It worked. We did it. What what was our solution, Kevin? We just tried many times and then waited to see what happened. Just wait until I cause many problems to my team.\u003C/p>\u003Cp>Okay.\u003C/p>\u003Cp>Speaker 0: On our screen. Okay. Alright. Gonna resume recording. Thanks for the click.\u003C/p>\u003Cp>Speaker 1: We got there. We did it. Go team. Go, Kevin.\u003C/p>\u003Cp>Speaker 0: Okay. Hubert, you're welcome. Alright. Ready to come back?\u003C/p>\u003Cp>Speaker 1: We're ready. Let's do it.\u003C/p>\u003Cp>Speaker 0: Okay. And welcome back. We fixed it. We don't know how we fixed it. It just started working again.\u003C/p>\u003Cp>We will undoubtedly come across this again. Having spoken to like our lead maintainer, he thinks it's a bug, which I will open a GitHub issue on after this recording. Okay. Wicked. So now determining the move and then returning the move.\u003C/p>\u003Cp>Okay, cool. So we determine the move, which just returns the next move. And then all we want to do here, the literally the only thing we want to do here is return last. Right? Data no.\u003C/p>\u003Cp>Last. Last? Yeah. Just last.\u003C/p>\u003Cp>Speaker 1: Yeah. I think just last.\u003C/p>\u003Cp>Speaker 0: Date data dot data dot last.\u003C/p>\u003Cp>Speaker 1: Is it? Alright. And the reason for this, if if nobody realizes this already, is because, like, you have to feedback this very specific JSON structure into the response that that Battlesnake precedes. Which kinda sucks. It just seems like, it should.\u003C/p>\u003Cp>It should just read this the engine should just get it. It should understand at a core level.\u003C/p>\u003Cp>Speaker 0: If you're if you're being told to implement an API specifically, it's fine to have to do it specifically, but we'll test it works. And in theory, we should be able to in theory, we should be able to add another another flow operation here to do logging. Any logging we want at the end of the whole run. So let's manage our battlesnakes. Let's create a game where I'd already set a battlesnake and a hungry bot an 11 by 11 board, and we'll start the game.\u003C/p>\u003Cp>And that should be running game in progress. I can kind of see my logs over here that the game is still happening. I'll wait for the game to conclude, which it has not yet. You know what? I've run out of patience.\u003C/p>\u003Cp>Oh, no. It happened. So let's hit.\u003C/p>\u003Cp>Speaker 1: There we go.\u003C/p>\u003Cp>Speaker 0: Excellent. It did end up doing itself in, so there is something wrong, but we're not we're not looking into that bit right now.\u003C/p>\u003Cp>Speaker 1: It worked. That's the important thing.\u003C/p>\u003Cp>Speaker 0: It it worked for some period. Anyway, that's all so it is it is talking successfully, to directors, which is useful. Now what we now wanna do is figure out what we need to log. And before we do this, let's create a collection for it. So we'll go to our data model, we'll create a collection and what are we going to log?\u003C/p>\u003Cp>I don't think we should log every turn. I mean, we could log every turn.\u003C/p>\u003Cp>Speaker 1: Well, I I mean, we could. I think I don't think it's necessary either. I think let's let's let's right now just log the final move just as like a getting started place. We're just gonna pass that data right through. And then let's do at like, ultimately, we'll end up adding something around, win or loss, because I think there's something cool visualizing that.\u003C/p>\u003Cp>And then I think there's honestly, I'd be love to explore us actually pulling the game GIF from this, which I think is more logic, but I think it's very interesting should be pretty straightforward.\u003C/p>\u003Cp>Speaker 0: So if we want to log this, if we wanna log the the game, we need to be doing this actually in the end endpoint. Right? We don't want to be doing this in the move endpoint. We want to do it in the endgame. So this actually means we need to a little bit more fiddling, a little bit more fiddling with directors.\u003C/p>\u003Cp>And to the dismay of our lovely editor, I'm actually gonna unshare and reshare, but I'm gonna do my whole screen this time because No camera job. It's too late. I'm doing it. It's done. It's gone.\u003C/p>\u003Cp>It's done.\u003C/p>\u003Cp>Speaker 1: Farewell. It's it's gone. Farewell screen.\u003C/p>\u003Cp>Speaker 0: Okay. Apologies for for for that. But what we have here I'm gonna just reconfigure also.\u003C/p>\u003Cp>Speaker 1: See every day, Kevin, by the way? Is this your screen, this large massive thing?\u003C/p>\u003Cp>Speaker 0: It's a 49 inch ultrawide monitor, which will feature in a future episode of What's in My Doc because the final episode of the season is yours truly. We talk about it more then. So if I just make this bigger, we want to we have this move endpoint here. We can delete all of this logic that was left actually. But we have let me just shrink this down a bit.\u003C/p>\u003Cp>Just a reminder, when we hit the root endpoint, we're gonna hit this flow. When we hit the move endpoint, we're gonna hit this flow, the start and there's end. So what we're gonna do is basically add this logic to to end. So what we're gonna do is, kind of similar to the info end point. You know?\u003C/p>\u003Cp>The info end point. Yeah.\u003C/p>\u003Cp>Speaker 1: Yep. Yep. Same thing.\u003C/p>\u003Cp>Speaker 0: Yeah. Although that is a get endpoint. We need a post endpoint. So it's more similar to move, I guess. But what we'll do is we'll create a new one.\u003C/p>\u003Cp>We'll call this snake, end. This can be based on a web hook trigger. It will be a post request. It is a post request. Right?\u003C/p>\u003Cp>I mean, we'll\u003C/p>\u003Cp>Speaker 1: find out together. I'm pretty sure it's a post request. Yeah.\u003C/p>\u003Cp>Speaker 0: We we will, but I'm also gonna I'm also gonna double check.\u003C/p>\u003Cp>Speaker 1: Look at the documentation. Probably smart idea.\u003C/p>\u003Cp>Speaker 0: Look at the documentation.\u003C/p>\u003Cp>Speaker 1: Not there though.\u003C/p>\u003Cp>Speaker 0: Webhooks. Here we are. Game over. It's a post request as an error hit, by the way. It's it's not formatted as a code block, but it's a post request to slash end.\u003C/p>\u003Cp>It will be the game object, the final turn number, which is useful, the board describing the initial state of the game board. That doesn't feel correct. Oh, on that final turn, I guess. And you describing your battle snake. And I wonder if there's a death reason, but we can find out.\u003C/p>\u003Cp>So response body date of last operation. Great. And first thing we're gonna do is just copy this URL. I'll hit save. We'll copy this URL and we will go ahead here and const flow response.\u003C/p>\u003Cp>I'm not sure we need to, respond to this, but we will Await fetch full URL. We're basically just gonna copy and paste the all of these options because they remain the same. And then we're just gonna send okay. We don't need to do like we actually don't care about the flow response. We're just gonna send all the data by fetch and then send okay.\u003C/p>\u003Cp>We just need the data. We don't and eventually to respond. So we'll save that.\u003C/p>\u003Cp>Speaker 1: Hear that flow response? We don't need you. We\u003C/p>\u003Cp>Speaker 0: don't need you. Yeah. So inside of our extensions, director snake, we'll run npm run build. Oh, in today's release of Directus, we squash the bug that stopped the autoreloading working. So That's awesome.\u003C/p>\u003Cp>Nice.\u003C/p>\u003Cp>Speaker 1: Was that because of us?\u003C/p>\u003Cp>Speaker 0: No. Not even remotely. Well, I'd like to think it is. 10.9. 10.9 came out yesterday, so it resolved that.\u003C/p>\u003Cp>So now we have our new extension. With this new endpoint, we don't need to do anything more there. Back over to our code editor here. I suppose we can just we can actually just leave this be. Yeah.\u003C/p>\u003Cp>And we can Yeah.\u003C/p>\u003Cp>Speaker 1: Let's just log, see if it shows\u003C/p>\u003Cp>Speaker 0: before. It. Yeah. Exactly. Create rematch.\u003C/p>\u003Cp>So that whole game is is running. We see that 200 all the way. Really low really low, response. That's great. Running, running, running.\u003C/p>\u003Cp>And then we now no longer, we remove the console log for end. Oh, this is on start. Have we done it in the wrong place? Everyone, we've done it in the wrong place. I know.\u003C/p>\u003Cp>Amateur hour. But it is logging it, so we know what we know what the shape would be. Yep. So we can do that instead. We'll make this async.\u003C/p>\u003Cp>We'll remove async here. We will go and rebuild the extension, and then we'll restart directives. And while that's kinda happening, we can we can take a look at this object here. So we have game, which is an ID, which we'll probably wanna store. The rule set perhaps, probably not for this one, The the final turn, 95.\u003C/p>\u003Cp>Don't know what we wanna know about the board probably because 95, it's the turn where you're dead. So that's that array will not include us or it will include us and we'll be the last one standing, I guess. You has health. So I wonder oh, maybe we won this one. Maybe we won this one.\u003C/p>\u003Cp>Let's, let's watch it. Have a sneaking suspicion we did win. Yeah.\u003C/p>\u003Cp>Speaker 1: So wait. How did we how do does it show that for us in that result? Like, it doesn't it doesn't say, like, in our in\u003C/p>\u003Cp>Speaker 0: the law No. No. But I I I wondered why is it still giving me a health of 90? Why did it give me a health of whatever it was, 83? Smart.\u003C/p>\u003Cp>I have a sneaking suspicion if you die, the payload looks different, but there's only one way to test it, which is I've now restarted Directus. If I refresh this, there should be some logs. So this was the log of the one we just accidentally ran when we shouldn't have. So this is the start. So we'll just forget that one.\u003C/p>\u003Cp>Let's, let's do a rematch. And now we should only get the response when when it dies. So we can see this. We see it's posting slash snake slash move. And the last thing it should do is post slash snake slash end and it should hit a different trigger.\u003C/p>\u003Cp>So we'll just watch that happening and this is happening real quick. Wonderful. And that is what happened. It had a different flow at the end slash snake slash end. So if we go back to our flow now and refresh, we should be able to look at the logs.\u003C/p>\u003Cp>Payload. Okay. So this oh, did we win or or lose? That's probably worth knowing ahead of time. You won\u003C/p>\u003Cp>Speaker 1: again. Come on, ready, set, battle snake. What are you doing winning all the time? You're too good. You're too good.\u003C/p>\u003Cp>Speaker 0: Too good. So 73 turn 73. That is us, ready, set, battlesnake. We know our ID. I wonder if there's something in here where we log what our own Battlesnake is, but it may not be necessary.\u003C/p>\u003Cp>Let let's keep looking. It has health of 92. It has a length of 6, which could be interesting. We don't need to know what all the food is, where all the hazards are. You is just a convenience again.\u003C/p>\u003Cp>Alright. Let's, let's literally just keep creating rematches till we die. We'll let this run-in real time which means we we're gonna have to wait a hot minute as every move happens. Are we too Lindsay.\u003C/p>\u003Cp>Speaker 1: Are we too good at programming Battlesnakes that he will live forever? Is that are we gonna need to are we gonna need to, pause?\u003C/p>\u003Cp>Speaker 0: Artificially. I'm gonna have to artificially make him just, like, die.\u003C/p>\u003Cp>Speaker 1: Come on. I think we oh my goodness. Come on. We gotta do it again. Gotta play again.\u003C/p>\u003Cp>Okay. Okay. Come on. 3rd time's the charm. We did, we did too much, Kevin.\u003C/p>\u003Cp>We went too hard.\u003C/p>\u003Cp>Speaker 0: That, game in progress thing is not behaving, so we'll restart a game this way. I mean, if I just add ourselves, eventually, we'll die.\u003C/p>\u003Cp>Speaker 1: Oh, there you go. Yeah. And it's interesting too. So, I think part of the game in progress thing might be I know I know Battle Sneak scaled down the server load, so it may be something related to that. Maybe.\u003C/p>\u003Cp>But, yes, we should live for we're gonna live forever. That's really what you've just done here. You're about to, you're about to kill your local version of Directus just because this will run forever. Hey. Good time.\u003C/p>\u003Cp>Alright. I immediately got it right and wrong. Thank you. Thank you. I reset Battlesnake.\u003C/p>\u003Cp>Alright.\u003C/p>\u003Cp>Speaker 0: I mean, eventually, we had to lose. Right? So let's look at the final log when we lose. And I have a hunch. It is that our battlesnake does have a health of 97.\u003C/p>\u003Cp>So there is a health in there. So that isn't how we determine whether we live or die, unfortunately. We have a board. We have you. Although, where are the oh, the snakes array is empty.\u003C/p>\u003Cp>So we are not so we are there. Sorry. But we are not present in the snakes array.\u003C/p>\u003Cp>Speaker 1: Oh, okay. That makes sense. So if the snake array is empty, we didn't win. If we are not empty\u003C/p>\u003Cp>Speaker 0: if we are not in it. Yes. If we are not in it. So the easy the easy thing to do here is we have a we know our snake ID. Here it is.\u003C/p>\u003Cp>We could store it in a collection of our snakes to like factor out or we could just hard code it in.\u003C/p>\u003Cp>Speaker 1: Well, let's let's let's have some fun. Let's let's do the more challenging\u003C/p>\u003Cp>Speaker 0: Sure. More challenging. Snakes. And then, actually, if we look at the if we look at the API, I don't think there is a a meta Battlesnake API is there to get information about games and stuff. No, that's fine.\u003C/p>\u003Cp>What else should what there could be. Yeah. What if there is there's a start endpoint. I wonder if no, it's fine. We'll we'll we'll hard code this snakes.\u003C/p>\u003Cp>All we care about snakes is that they have a manually entered string as the ID and we can add all the others later. So if we go to our snakes, we'll put in the ID. That's ours. And you know what? We'll just add a name to it just so we can tell what it is.\u003C/p>\u003Cp>Right? So, this battle snake is ready, set, battlescape, I actually think is its actual name because we wanted to distinguish the one you made to the one I made. And my name is Elsford k. So it's registered Battlescape. Okay.\u003C/p>\u003Cp>Cool. So we've got that. So what are we gonna do inside of this end flow? First thing we need to do, I suppose, is we get this whole payload. The only thing we wanna enrich it with right now is are we alive?\u003C/p>\u003Cp>So Yeah. Is alive. Did live. Did I live? And that's going to run scripts.\u003C/p>\u003Cp>No, it isn't. First of all, we're gonna get all of our snakes. That's what we should do. Read items from the database. So, get snakes.\u003C/p>\u003Cp>Permissions from the from trigger. I'm just gonna give it full access so we don't need to contend with permissions. Snakes. If I don't add any IDs, do I get them all?\u003C/p>\u003Cp>Speaker 1: We'll find out. We gotta find out.\u003C/p>\u003Cp>Speaker 0: This is\u003C/p>\u003Cp>Speaker 1: trial and error, Kevin. This is trial and\u003C/p>\u003Cp>Speaker 0: error. Real, real trial and error thing going on here. Alright. Let's create a rematch with ourselves. I feel like I should add another snake back in just to end the games quicker.\u003C/p>\u003Cp>Speaker 1: So do it. Yeah. Do it. Add another snake in. Just go back and\u003C/p>\u003Cp>Speaker 0: make sure I'll let I'll let this game conclude. We'll do it until the next time we do\u003C/p>\u003Cp>Speaker 1: it.\u003C/p>\u003Cp>Speaker 0: Still running.\u003C/p>\u003Cp>Speaker 1: By the way, it looks\u003C/p>\u003Cp>Speaker 0: like games. Oh, thank you. I actually was really trying to find, like, the end of Loki season 2, all of the timelines. I'm not sure if you've seen it. And so it was kind of Yes, I've probably seen it.\u003C/p>\u003Cp>I've seen it's going for. Yeah. It was kind of that vibe I was going for. Okay. So obviously we eventually die.\u003C/p>\u003Cp>That's just the nature of playing by yourself. Let's just see how they perform now. Okay. Again, no idea why. Really shouldn't be doing that.\u003C/p>\u003Cp>But\u003C/p>\u003Cp>Speaker 1: Don't question it. We wanted that to happen. We willed it into being. We willed it to happen.\u003C/p>\u003Cp>Speaker 0: We want you to be chitter. Alright. There there's all of our snakes. So that's great. So that that returns an array of objects which contain IDs which are our snakes.\u003C/p>\u003Cp>So the next thing we want to do then is did live, like, you know, did live. And we will run a script here. And all we wanna determine here is, what did we call the last one? Snakes. So data dot snakes is the array of snakes and we want to know and we want to know did the trigger dot I need to I don't want I don't wanna close it.\u003C/p>\u003Cp>Don't wanna close it to to take a look. Let's look at the docs. Webhooks. So it will be trigger dot, I think, payload.\u003C/p>\u003Cp>Speaker 1: I think so. Yeah.\u003C/p>\u003Cp>Speaker 0: Yeah. We can always fix that later. The payload.board.snakes. So we wanna see, did that contain that? So, JavaScript, does array contain other does array contain specific item?\u003C/p>\u003Cp>I think it's like includes yeah. Does array contain item from other? Right? I'm pretty sure we just did this in our last episode.\u003C/p>\u003Cp>Speaker 1: I think so. Yeah.\u003C/p>\u003Cp>Speaker 0: Some. And I think I used find instead. So, live, which is like the if it lives, it will be found. So we'll say is inside of data dot snakes, is does that contain yes. Does that contain does data.snakes contain?\u003C/p>\u003Cp>And this is where it gets a bit honky, and I need to just just have a moment to think. So we wanna know out of all of the items in the snake array of which of which we know the I the ID is gonna be snake dot it. We know that already. Does is snake ID equal or does snake ID I got it. Does snake ID feature inside of this array?\u003C/p>\u003Cp>So what we actually wanna do is return whether this array, the one with all the snakes on the board contains, which I think we've determined is includes includes.\u003C/p>\u003Cp>Speaker 1: Yep.\u003C/p>\u003Cp>Speaker 0: Is it includes? I think we need to simp I think we need to do one more step beforehand which is take this big array here and just simplify it down to IDs. So, const IDs, IDs on board. So we'll take all of that and we'll map it and all we wanna do is turn that into an array of IDs. So it's just IDs.\u003C/p>\u003Cp>It's nothing else but IDs. Forget all the other information. That's all of the boards. We don't That's all of the IDs of the snake on the board. Exactly.\u003C/p>\u003Cp>And then we will return whether or not IDs on board that's it. IDs on board includes any of our snakes IDs and we will return to live. So true force, true false, did it. Really?\u003C/p>\u003Cp>Speaker 1: It's gonna work. First time's the charm.\u003C/p>\u003Cp>Speaker 0: Oh, no. I've done it again. I've hit create rematch, But eventually the answer will be false, right, this time with confidence. It should be. Or it'll just live forever and this will be this will be our time.\u003C/p>\u003Cp>Okay. Hit its hit its own tail. Mounted itself into a corner and did itself in. So let's, look at the last log. Cannot read properties of undefined.\u003C/p>\u003Cp>Okay. Cool. And it was body, by the way. It was trigger dot body. So that will probably be why.\u003C/p>\u003Cp>Trigger.body.board.snakes.map. So let's, let's just check that. Body dot board dot snakes. Cool. So we will create rematch.\u003C/p>\u003Cp>Speaker 1: And now second time.\u003C/p>\u003Cp>Speaker 0: And now.\u003C/p>\u003Cp>Speaker 1: Go, red snappettlesnake.\u003C/p>\u003Cp>Speaker 0: So stop eating food. Stop eating food. As it gets towards the food, I'm like, no.\u003C/p>\u003Cp>Speaker 1: Come on. What are you doing? We don't want you to live anymore. What are you why?\u003C/p>\u003Cp>Speaker 0: Why are you doing this? This battle snake is not bad apart from those times it randomly goes off the edge of the board, and we don't know why. I think it's just got\u003C/p>\u003Cp>Speaker 1: a it's got a it's got a spirit of its own.\u003C/p>\u003Cp>Speaker 0: Oh, what's happened there? Oh, there we go. Oh, okay.\u003C/p>\u003Cp>Speaker 1: There was some real They\u003C/p>\u003Cp>Speaker 0: were clearly in a queue or something.\u003C/p>\u003Cp>Speaker 1: Goodness. Wow. We're doing I Oh, there we go. Why did you do\u003C/p>\u003Cp>Speaker 0: that? I don't know. Oh, yeah. Yeah. Yeah.\u003C/p>\u003Cp>Right. It knocked itself into a into that kind of spot where where it can't. There is no viable move. Right. So we'll look at this.\u003C/p>\u003Cp>Cannot read properties of undefined. Okay. Okay. What is undefined? What's undefined is what we're running find on.\u003C/p>\u003Cp>So let's take a look at that. We're running find on data dot snakes. Okay? Except oh, because we call it get snakes. Fair.\u003C/p>\u003Cp>We'll call that snakes. Great rematch. This is the only thing when you'll see ready. Set up\u003C/p>\u003Cp>Speaker 1: the snake. Sorry. This is the only thing when you're debugging. Go on. And you were saying something valuable as part of this conversation.\u003C/p>\u003Cp>I was trying to add\u003C/p>\u003Cp>Speaker 0: some color. Wait. You there's there is just a bit of waiting here. Right? I suppose because the rules engine is open source.\u003C/p>\u003Cp>I imagine people I think people have built, like, line emulators that can obviously execute this way, way, way quicker for the sake of debugging. And I see the value in those tools.\u003C/p>\u003Cp>Speaker 1: Yeah. You can actually do it in the CLI. In the CLI, you can actually play full games and see them.\u003C/p>\u003Cp>Speaker 0: Because we just had to weigh a 153 turns. Alright. Trigger is not defined. Still. Okay.\u003C/p>\u003Cp>Trigger is and you know what? Because it's data dot trigger. So many amateur amateur hour little mistakes today. Never.\u003C/p>\u003Cp>Speaker 1: Never, Kevin. Amateur is, is a term that we need to take back as a positive\u003C/p>\u003Cp>Speaker 0: As amateurs.\u003C/p>\u003Cp>Speaker 1: As amateur. As amateurs, yeah. Alright. Come on, ready set battle snake. You can die.\u003C/p>\u003Cp>Don't survive.\u003C/p>\u003Cp>Speaker 0: Please don't.\u003C/p>\u003Cp>Speaker 1: I believe in I I don't believe in you. I feel like he thrives.\u003C/p>\u003Cp>Speaker 0: So the weather so the weather today.\u003C/p>\u003Cp>Speaker 1: Yeah. Oh. Oh. Is that is this it?\u003C/p>\u003Cp>Speaker 0: No. No. It it will lit. It's just obviously saving up moves. Yeah.\u003C/p>\u003Cp>There we go. Woah. It's like a taunt. That was like a taunt to us.\u003C/p>\u003Cp>Speaker 1: Come on.\u003C/p>\u003Cp>Speaker 0: We're doing exactly as we the thing is we made we made this snake. We made this snake.\u003C/p>\u003Cp>Speaker 1: I feel like we is a very, oh, there we go. Alright. He made himself, really. Alright. Let's see if this worked this time.\u003C/p>\u003Cp>What do our logs say? Hey. We got data.\u003C/p>\u003Cp>Speaker 0: Yeah. But this isn't what we want. What do you mean? What we want. I don't know, but I just want, I just want, I just want a true false value so something's not right.\u003C/p>\u003Cp>Speaker 1: But wait\u003C/p>\u003Cp>Speaker 0: So, yeah, go on.\u003C/p>\u003Cp>Speaker 1: Well, I'm just like, what are we returning? Sorry. It's the text\u003C/p>\u003Cp>Speaker 0: is really small.\u003C/p>\u003Cp>Speaker 1: Not because of your\u003C/p>\u003Cp>Speaker 0: screen. It's because We are returning, the whole, like, body of the script, which feels a little bit weird. And I have a hunch. I have a hunch. It's oh, no.\u003C/p>\u003Cp>Did live. This is live. So what's going on? What's going on there?\u003C/p>\u003Cp>Speaker 1: Super weird. That shouldn't be returning that.\u003C/p>\u003Cp>Speaker 0: No. It should not. Let's, look at our previous flow, the move one, and just maybe not, like just take a little bit of yeah. I mean, if we take all of this, and I'm just doing this as a comparison point just so we can kind of dump it in here and take a look kind of side by side, if you will. Yeah.\u003C/p>\u003Cp>Data dot trigger, Return. We do just return a value. Maybe, let's take a look at, array find method. Let's just understand this a little bit better. This returns the 1st element in the provided array that satisfies the provided testing condition.\u003C/p>\u003Cp>If you need an index, you can use find index. If you need to find the value, if you need to find if a value exists, you can use includes, which actually I think is what we want because that just returns a true or false. So maybe we just swap this out for an includes, includes, And we wanna know yeah. It's like a nested includes. Fine.\u003C/p>\u003Cp>And then we return I'm just I'm a little bit confused why it was returning\u003C/p>\u003Cp>Speaker 1: Yeah. The whole code.\u003C/p>\u003Cp>Speaker 0: Well, we no. You know what? We also don't get here. We don't get a returned payload, which makes me think there was some kind of error happening here that wasn't being captured correctly. Is there anything in here?\u003C/p>\u003Cp>I wouldn't imagine there is, and it's now really hard to tell. Let's, let's create a game. Let's let's make this game a lot, a lot quicker to end.\u003C/p>\u003Cp>Speaker 1: Hey. There we go.\u003C/p>\u003Cp>Speaker 0: Well, do you see how quick that was?\u003C/p>\u003Cp>Speaker 1: That was fast. We are smarter than hungry.\u003C/p>\u003Cp>Speaker 0: Niamis.\u003C/p>\u003Cp>Speaker 1: Come on. What are you doing, Raze at Battlesnake? We want you to not die. We just want you to die.\u003C/p>\u003Cp>Speaker 0: We might we might have to nip out some of the logic or something.\u003C/p>\u003Cp>Speaker 1: I've never had a\u003C/p>\u003Cp>Speaker 0: Well, well, that will that that will be the end. That will be the last move when it finally catches up with that with with Toast. Yeah. Snake end. The game seems to be struggling with the concept of ending, but it has ended.\u003C/p>\u003Cp>Alright. Yeah. Look. We get the options here, but we don't get the payload. Because if you look at the other flows, for example, wow, 1632.\u003C/p>\u003Cp>Wow. You see that all of these run scripts. I know they have the payloads. They have the options and they have a payload. Options payload, determine move interestingly interestingly, does does not have a returned payload.\u003C/p>\u003Cp>Is there something about them returning objects and strings versus just returning like a primitive. Maybe that's what it is. So if I so if I because this could be fine, you know, return. That's returning an object. Right?\u003C/p>\u003Cp>Mhmm. Let's create a rematch. Okay. Our snake just went up and up again. Hey.\u003C/p>\u003Cp>We don't know why happened.\u003C/p>\u003Cp>Speaker 1: Bad things happened. We changed we changed the code. Shouldn't have done that.\u003C/p>\u003Cp>Speaker 0: Changed the code. Shouldn't have done oh, there we are. Did live force. Okay. Awesome.\u003C/p>\u003Cp>Cool. So so there was actually no problem. Just an interesting note there that for it to show up in the payload, it needs to be an object or an array, I presume. So just a good just a good note there. I would love to play a game where we don't lose.\u003C/p>\u003Cp>So we can just so we can just check both sides of that equation and that they make sense. Let's create small boards.\u003C/p>\u003Cp>Speaker 1: If we play with ourselves It wouldn't then technically, we'll always win and always win.\u003C/p>\u003Cp>Speaker 0: Oh, well, yeah. But then I think it's just gonna get a little bit weird. There we are. Hey. We no.\u003C/p>\u003Cp>Wait. That's not what we want.\u003C/p>\u003Cp>Speaker 1: That's what we want. But We've wanted to lose so much that now to win seems like a wrong thing.\u003C/p>\u003Cp>Speaker 0: So did live should be forced. Yeah. Live should be forced. Let's create a rematch. Maybe we need to put in a a a a less clever snake.\u003C/p>\u003Cp>Goodness. Okay. Try Do you want the right bot? Yeah. Yeah?\u003C/p>\u003Cp>We we haven't we haven't gotta live long to make that work. Okay. Great. So now if we refresh, we look at the logs. Did live hopefully should be\u003C/p>\u003Cp>Speaker 1: False. Absolutely. That's totally what we were wanting to have happen.\u003C/p>\u003Cp>Speaker 0: Great. Is this really what we're gonna spend this episode doing? Okay. Fine.\u003C/p>\u003Cp>Speaker 1: That's it. Just debugging JavaScript. That's really what we're here for.\u003C/p>\u003Cp>Speaker 0: Right. All we want to do is now maybe maybe we just roll back for a sec. We're doing all of this grab snakes thing. I don't think this is adding I've deleted it. I've just done it.\u003C/p>\u003Cp>I've just done it. I've been busy for decision. Exec executive decision. All we care about is this ID. Right?\u003C/p>\u003Cp>So let's just we'll we'll delete this. We're gonna\u003C/p>\u003Cp>Speaker 1: hard code it in now. Okay.\u003C/p>\u003Cp>Speaker 0: Yeah. Yeah. Like, I don't think it's worth, like, going at all costs. Right? So all we want to know is does data dot snake say const?\u003C/p>\u003Cp>In fact, I think we can say, does snake include data dot trigger dot body dot board dot snakes dot map. Sure. So that's just a set of IDs. And all we wanna know is, does IDs onboard include this? And I'm I'm assuming can I console log inside of these?\u003C/p>\u003Cp>Best bit of, debugging we got. Great rematch. Well, I think our snake's broken. I think we spoke too soon about the capability of our snake. In it doesn't like includes.\u003C/p>\u003Cp>Payload.body. Right. Hang on. It doesn't like includes, which is this one here. And we don't get a log, annoyingly.\u003C/p>\u003Cp>So all we care about, Let's, you know what? I'm actually gonna be bold here and just remove this, and we can start we can start again. So so all we wanna know is did it live? And to know if it lives, we're gonna go through oh, no. I do need to I need this too.\u003C/p>\u003Cp>Speaker 1: I like it. I like the I like that you were confident in the removal,\u003C/p>\u003Cp>Speaker 0: but also\u003C/p>\u003Cp>Speaker 1: truly confident. That I you\u003C/p>\u003Cp>Speaker 0: know, I really wasn't. So all you wanna know is, does data dot trigger dot body dot board dot snakes, does that include includes includes let's take a look. We need to give it a specific value here. So I think what we'll first do is we'll say snake IDs on board. We'll take this and we'll map it once again.\u003C/p>\u003Cp>So we just extract the IDs. Then all we wanna do is check whether snake IDs on board includes this value here. It's all we wanna know. Please work. Create rematch.\u003C/p>\u003Cp>Speaker 1: We've got this.\u003C/p>\u003Cp>Speaker 0: Okay. Our snake just keeps going up by the way. We fundamentally broken something.\u003C/p>\u003Cp>Speaker 1: But I mean, these things happen. These things happen. We'll figure it out.\u003C/p>\u003Cp>Speaker 0: We've got Live force.\u003C/p>\u003Cp>Speaker 1: The rest of today.\u003C/p>\u003Cp>Speaker 0: Live force. Okay. And we did we did we did live force. Yes. That is that is true.\u003C/p>\u003Cp>And all we need to do is outlive the snake, which we did, and hopefully live is true in this context. We may need to go back as as we commented in Okay. But we lived, but we lived. Let's let's take a look at the payload. Let's take a look at the payload.\u003C/p>\u003Cp>So body dot game, no. Body dot board dot snakes has ready set battlesnake present, and that's it. The snakes have an ID. That's our ID. That is I'm just gonna double check.\u003C/p>\u003Cp>Yep. The ID, nope. Not the ID we've been pasting in. What does it get a different does it well, it's different. So I'm wondering, look at this ID, w d at the end.\u003C/p>\u003Cp>If I look one game back, it gets a different ID every time, Andrew.\u003C/p>\u003Cp>Speaker 1: So we gotta pull name then, I guess. I guess the ID is not\u003C/p>\u003Cp>Speaker 0: I'm literally so mad. Yes. Fine.\u003C/p>\u003Cp>Speaker 1: As well, you should be. Damn you, battle stake and your your ID that is not truly the snake ID. Who do\u003C/p>\u003Cp>Speaker 0: you It does feel that way. I can't believe we sunk so much time into that. Okay. Cool. No.\u003C/p>\u003Cp>Right. Create custom game. Create rematch. Okay. We lost because apparently we just go up now.\u003C/p>\u003Cp>Yeah. Liveforce. Cool. And create rematch. Yeah.\u003C/p>\u003Cp>I mean, we lost again. Sure. Because that's what's happening now. What is going on with that, by the way? What is going on with that?\u003C/p>\u003Cp>Is it is it work. Are we struggling under the number of logs that are being generated?\u003C/p>\u003Cp>Speaker 1: No.\u003C/p>\u003Cp>Speaker 0: I mean, possible. Look, avoid head to head force. So there's something in the avoid head to head. Oh, god. Because there was no head to head issue here.\u003C/p>\u003Cp>No. There wasn't. Let's create rematch just until we get a a seed. I think, yeah, we both die at the same time there. Let's create rematch.\u003C/p>\u003Cp>Let's just create a seed just to test out this logic. Yeah. That's good. Nope. Not good.\u003C/p>\u003Cp>Okay. Create game. Okay. We we broke it. We broke it.\u003C/p>\u003Cp>Okay. We are still not gonna do well this oh, what is going on? There we go. Is it\u003C/p>\u003Cp>Speaker 1: there someone\u003C/p>\u003Cp>Speaker 0: is there something is there something about we did? Is there something about the board size being different that's throwing it off? In which case, I'm quite happy to say how Battlesnake only operates in 11.\u003C/p>\u003Cp>Speaker 1: That's the right answer. Don't make it work on all the boards. Only standard class.\u003C/p>\u003Cp>Speaker 0: True. Okay. Hey. There we go. Finally know whether it lives.\u003C/p>\u003Cp>Now it's all about logging information. So let's look at this payload and see what we have. So we have a game ID, which I think feels like a no brainer. Right? Yeah.\u003C/p>\u003Cp>And you said we can do something with the ID to get the GIF?\u003C/p>\u003Cp>Speaker 1: I believe so. I'm gonna go check and see something right now. I'm gonna go over while you're doing this, I'm gonna go over to my other screen. But, yes, we'll pull back so that we need that.\u003C/p>\u003Cp>Speaker 0: We know the turn of the final move, which is 2. Yep. Right. So that's the term where we were victorious. We know that we lived.\u003C/p>\u003Cp>We know our length, I suppose.\u003C/p>\u003Cp>Speaker 1: Yeah. I think so.\u003C/p>\u003Cp>Speaker 0: Yeah. And maybe we do maybe we start with just games. Games get an ID, which can be a manually entered string because we'll we'll pop that in from the API. And I think all we need here is win or lose, right? Win.\u003C/p>\u003Cp>That's a Boolean. We wanna know what about it. Like, a turn, I suppose. That can be an integer.\u003C/p>\u003Cp>Speaker 1: I mean, game mode? Turns. Yep. Turns.\u003C/p>\u003Cp>Speaker 0: Turns feels better. Yep. Turns. Into game mode, but we're not playing around with other game modes. So I just think That's true.\u003C/p>\u003Cp>Yeah. It's fair. Yeah. But but the idea is, in theory, you could you could store all of this. Wind turns.\u003C/p>\u003Cp>What else have we got at our disposal? Let's take another look at our end state here. We have live die. We had turns. We can start with how many snakes there were on the board from the trigger.\u003C/p>\u003Cp>So snakes dot so trigger trigger body board snakes length. Just quickly gonna go, store. We're gonna prepare data. So trigger dot body dot board, did we say it was? Dot snakes.length.\u003C/p>\u003Cp>So I'm just gonna pop that in there for later. Yeah. So snakes, that can be another integer. What what else what else do we wanna know?\u003C/p>\u003Cp>Speaker 1: I mean, let's let's let's stick it with that. Good because we\u003C/p>\u003Cp>Speaker 0: you could do, like, a win ratio. You could do, like, average turns per game. Like there's lots you can do here. Do you continue getting data if you are not the last to loot? Like if you die and it goes on a few more steps, do you only get the end state on your final turn or do you like, let's take a look.\u003C/p>\u003Cp>Your battle snake will whenever a game it has playing has been ended, use it to learn how you battle snake won or loss and deallocate any server side resources. Okay. This was like a good starting point. So this is what a game looks like. ID win turns snakes.\u003C/p>\u003Cp>So going back to flows, let's, do our end. So did live. We're gonna prepare data. So the only extra thing we wanted to know here was snakes. Snake count.\u003C/p>\u003Cp>What else do we wanna know here? We we might as well get it already to be fair. Might as well just do it straight within here. So snake count. Is this.\u003C/p>\u003Cp>We have we had snake count. We had turns. So once again, let's just take a look at the it was game. I think it's game dot turn. Game?\u003C/p>\u003Cp>Maybe not. Bored?\u003C/p>\u003Cp>Speaker 1: Bored? Bored?\u003C/p>\u003Cp>Speaker 0: Hold on. No. Am I using it? Oh, it's right here. At the root, turn, integer.\u003C/p>\u003Cp>So that should just be trigger.body.turn. And we wanna know we wanna tell how many things. We wanna tell how many turns. We wanna tell whether we lived or died that we needed the ID. ID.\u003C/p>\u003Cp>So trigger dot body dot the ID. I think it's just I game dot ID, I think. We'll find That sounds right. Game dot ID. Yeah.\u003C/p>\u003Cp>Nice. And while we're here, let's, let's just also get the did live. So live. Live? What did what did we call it in our director's collection?\u003C/p>\u003Cp>Speaker 1: Lip libs? Let's take a look. Was just libs?\u003C/p>\u003Cp>Speaker 0: It's called win, actually. Win. Alright. Smart. Win.\u003C/p>\u003Cp>So, oh, so in here, we'll go prepare data. We'll go live, but it's called win here. Live, live. Fine. Whatever.\u003C/p>\u003Cp>So we have, I really don't like using last or I can help it because it allows you to obviously move things around without your data becoming like brittle. So I'm gonna pop did live. And all of these have to be prepended with data. Okay. So in theory, that's the preparation of the data.\u003C/p>\u003Cp>Let's, let's run a custom game one more time. If our snake performs well, it will have been the 7 by 7 board that did it in. Well, I think I think oh, of course, we've just got let's create the game with the ready set battle snake and the hungry bot now. Yeah. Weird, isn't it?\u003C/p>\u003Cp>Anyway That's such a good idea.\u003C/p>\u003Cp>Speaker 1: I wonder what we did. Don't don't wanna know. No. That's a question for, for season 2.\u003C/p>\u003Cp>Speaker 0: That was a question. Set back. That was a question for last week of and now it's this week of do you see how big that snake's getting? Oh, of course, it's hungry, but isn't it? That's its whole that's its whole shtick.\u003C/p>\u003Cp>Speaker 1: Are we gonna win this one? Oh, we won it still.\u003C/p>\u003Cp>Speaker 0: Yes. Legit did. So Well if we refresh here and look at our log payload, snake count 1. This is wrong. So we need to just look at this because we wanna know how many snakes were in the whole game, which in theory should always be\u003C/p>\u003Cp>Speaker 1: no. It won't always be this. End. We're pulling from end, so it's only gonna pull the last snake. Right?\u003C/p>\u003Cp>Is that what we that's what\u003C/p>\u003Cp>Speaker 0: the Yeah. Maybe we maybe we need to do it on start. We need to create it. And then on end, we need to update it. We have turns, we have the ID and we have whether we want, which is true.\u003C/p>\u003Cp>So maybe for snake count, because I do think snake count is interesting. We should create just create the data. What do you think? Or do you think it's too heavy handed to create a whole flow for that?\u003C/p>\u003Cp>Speaker 1: I mean, I don't think we need a whole flow for it. That seems like a bit of\u003C/p>\u003Cp>Speaker 0: a change. I mean, we need we need a flow for it. That's the truth. So may I mean, we we've we've got pretty we've got pretty proficient at it now. So what we're gonna do is create a flow.\u003C/p>\u003Cp>Yeah. Yeah. We'll call it snake start, Right? And so what we're gonna do here is we're gonna do this as a webhook. It'll be a post request.\u003C/p>\u003Cp>We'll go like this. Just double check the API reference webhooks. Your request to this your response to this request will be ignored. We don't need to care about our response. We'll save this, grab this URL, and then we are going to take this whole end here.\u003C/p>\u003Cp>And on the start, we'll make that async. We'll paste that in there, except this time we will change the URL to this 1, 704. So that start, we need to just kill this, go here, rebuild our extension, Done. Go here and Docker Compose up. Wonderful.\u003C/p>\u003Cp>Should be able to refresh. Should be able to refresh. Now I think it just took a moment just a moment to spin up there. And what are we gonna do here? We're first gonna save.\u003C/p>\u003Cp>We're gonna go and create a game. This will need we don't even need to wait for the game to pan out in order to look at this one log. All we care about is I suppose number of snakes here. So trigger, body, board snakes. Trigger body board snakes.\u003C/p>\u003Cp>So what we'll do here is create sorry? Board trigger body snakes. No. Trigger. Shouldn't be Stop it.\u003C/p>\u003Cp>Shouldn't be Stop it.\u003C/p>\u003Cp>Speaker 1: I shouldn't do that.\u003C/p>\u003Cp>Speaker 0: I'm sorry. Create game. We will create data in the game's collection with full access. And all we're gonna do here is we're gonna say the snakes is equal to.\u003C/p>\u003Cp>Speaker 1: Trigger body board snakes. I got it. I got it.\u003C/p>\u003Cp>Speaker 0: They need to be in quotes because it's Jason. Trigger\u003C/p>\u003Cp>Speaker 1: Jason or Jason? I I am a JSON.\u003C/p>\u003Cp>Speaker 0: Jason. There is one there is actually, extract snake count. We just need to get the value out in the previous step. I don't strictly think you need to do this, but just to keep it really clean, what we're gonna do here is we're gonna return snake count. Snakes is equal to data dot trigger dot body dot board dot snakes dot length.\u003C/p>\u003Cp>Snakes. Copy that key. And then the next one, we will create data. The name and key doesn't really matter because it's our last step here. All we care about here is we need to create an ID at the time.\u003C/p>\u003Cp>Oh, but we can create the ID as well. We can get the ID as well at the time of creation. So snakes is going to be equal to, this snake count dot snakes. And I actually think we can ignore this error. There seems to want a string, and I think that's fine even if it's, an int just to stop it moaning.\u003C/p>\u003Cp>And then we also want the ID, which is going to be oh, oh, throwing my cursor around. Here we are. Trigger dot body dot game dot id. So if I save this, we create a new game. Oh, got a little Game in progress.\u003C/p>\u003Cp>I don't think it is. Our snake 1, thank you very much, but we will create a new game. That's fine. Start game. We immediately should get a new log here and we got a trigger but nothing else happened.\u003C/p>\u003Cp>Why? Oh, no. There we are. Create data. Type error cannot read properties of undefined snakes 2 body trigger body game ID.\u003C/p>\u003Cp>Okay. Cannot read properties of undefined reading starts with. Interesting. This looks\u003C/p>\u003Cp>Speaker 1: Looks good.\u003C/p>\u003Cp>Speaker 0: Looks like what we want. Snakes and ID. Is that what we called them? Snakes and ID. What is going on?\u003C/p>\u003Cp>So star. Shrek snake count. Because in theory, this is looking oh, pick collection. That'll do it for the access. Not telling it to do anything.\u003C/p>\u003Cp>Alright. Create game. I've got really you get really proficient at starting new games with Battlesnake, don't you, when debugging? You do. Alright.\u003C/p>\u003Cp>Speaker 1: Did it work? Oh, there we go. Now how That's\u003C/p>\u003Cp>Speaker 0: the idea of a new item, item, which means we now have a new snake. Wonderful. So now going back to our end state, we don't actually want to so let's refresh ourselves. Prepare data, prepares a snake count, turns, ID and win. We do not want a snake count.\u003C/p>\u003Cp>We do not want a snake count. So when we prepare data, we can remove snake count and that leaves us with turns, ID, and win. Yep. Turns, ID, and win. Turns ID and win.\u003C/p>\u003Cp>And, actually, we don't even want the ID here.\u003C/p>\u003Cp>Speaker 1: No. We we do want the ID because we wanna be able to pull that GIF eventually.\u003C/p>\u003Cp>Speaker 0: But we stored it. It already exists.\u003C/p>\u003Cp>Speaker 1: Oh, that's right. Yes. You're right. You're absolutely right.\u003C/p>\u003Cp>Speaker 0: This is what we're gonna This is what we're gonna so all we're gonna do here is now update the data Lovely. In the game's collection with full access with this ID, trigger body game ID, and the payload, I think we might just be able to put in lost and I think that's gonna be equal. I'm not a 100% sure, but I think that's gonna be equal to the object. So we'll see. Alright.\u003C/p>\u003Cp>The first thing we'll do is create a new game, and then I think we'll be ready to jump into insights and see how that works for us. So here's our snake performing well in these apparently quite controlled conditions of an 11 by 11 board with 1 other snake. Okay. Come on. Some something happened now.\u003C/p>\u003Cp>Speaker 1: Come on, hungry. You don't wanna survive. You're not really that hungry anymore.\u003C/p>\u003Cp>Speaker 0: It could be all all one either. You both suck. Hey. Hey. So first thing to do is let's see.\u003C/p>\u003Cp>So you will notice nothing happened there. I've also, to be fair, noticed we don't have any we just have IDs. We probably do also wanna capture, like, the date, like like the now date that's happening because, we just wanna be able to sort the latest item if possible. Yeah.\u003C/p>\u003Cp>Speaker 1: That's fair.\u003C/p>\u003Cp>Speaker 0: So we'll create a date time, finished. We can call this. I think this will work. Date time. Sure.\u003C/p>\u003Cp>And then at the well, firstly, that didn't work. So we don't wanna spend too too much time on this, but we'll prepare the data here and we'll say what was the name of the field? Do you remember?\u003C/p>\u003Cp>Speaker 1: Oh, it was, we didn't call it datetime. We set it today. No. Now? When?\u003C/p>\u003Cp>I'm not sure. Jeez. That that's terrible. Both have forgotten Finished. The minutes of creating it.\u003C/p>\u003Cp>Speaker 0: I mean, same. Same to be fair. Finished. We also wanna store finished, which I think you can just say new date, new date dot now. Is that how you do it in JavaScript?\u003C/p>\u003Cp>Speaker 1: Your guess is as good as mine, sir.\u003C/p>\u003Cp>Speaker 0: Yeah. New date dot now. Cool. But it didn't work. So as much as this is cool, what happened here?\u003C/p>\u003Cp>We updated it. We updated it with the key. Oh, do you wanna know what didn't happen? It isn't showing here that we had a payload. So I think there was an issue with the payload.\u003C/p>\u003Cp>Oh, look, the payload didn't save. Interesting. So I'm just gonna double check that that is what happened there. If I hit save and then step back in, maybe it shouldn't let me do that, but this is okay. We're not gonna lose much sleep over it.\u003C/p>\u003Cp>Finished is going to be, no, last dot finished. Then we had, win is going to be last dot win. We'll wrap that in quotes too. What was the third one? What what are\u003C/p>\u003Cp>Speaker 1: the 2 that we have now?\u003C/p>\u003Cp>Speaker 0: Win, turn Yeah. Turns. I believe. Like\u003C/p>\u003Cp>Speaker 1: a living rubber duck.\u003C/p>\u003Cp>Speaker 0: Last stop turns. Turns. I'll hit save before we, you know, spend time on a full snake. We'll take a look. Turns and win.\u003C/p>\u003Cp>Turns and win. And now we have finished. Turns, win, finished. Turns, win, finished. Great.\u003C/p>\u003Cp>So let's save all of that. Let's create a rematch. We should see, hopefully, that this is stored. And then I don't think we're gonna spend much more time capturing data. Instead, we'll run a few games, and then we'll try some insights.\u003C/p>\u003Cp>Nothing's happening still. So still something done in our logic. Let's look at our latest I'll just refresh it. Let's look at our latest log. Oh, intermediate value dot now is not a function.\u003C/p>\u003Cp>I actually know why this is the case, and it is because, these run script operations running what are known as JavaScript isolates, which are these incredibly limited JavaScript like runtimes that only contain pure JavaScript objects. Let's actually take a quick look at that because I think it's interesting. Isolut. Yeah. This page looks legit.\u003C/p>\u003Cp>That's not what I want. I do kinda wanna show you like pure Java script or, objects. This is what I'm looking for. What I'm looking for is just like, where is the list? Am I am I what's happening?\u003C/p>\u003Cp>You might\u003C/p>\u003Cp>Speaker 1: be imagining. You might be imagining this. You may have already used JavaScript.\u003C/p>\u003Cp>Speaker 0: I just I have imagined it.\u003C/p>\u003Cp>Speaker 1: Created it. Maybe you created. There's no documentation. What did you call them again? Isolates.\u003C/p>\u003Cp>Isolates. Interesting. I've never\u003C/p>\u003Cp>Speaker 0: heard of that. Isolates.\u003C/p>\u003Cp>Speaker 1: And now is that just a security thing within Directus to ensure that, like, bad actors\u003C/p>\u003Cp>Speaker 0: So this So this actually happened in version 10.6. We had I think this is quite what I want. We had prior to version 10.6, you could build, you could use NPM modules inside of extensions. Extensions. Well, video has gone a bit funny.\u003C/p>\u003Cp>I'm not sure if the is fine now. It resolved itself. Anyway, and the library which we use to isolate flows from each other and operations from each other basically became insecure. Like, it was it was became known it was insecure, and so we had to remove it. And in doing that, we replaced it.\u003C/p>\u003Cp>And now it now it runs what are known as isolates. And this is what I'm looking for, I think. 1st class. Nope. I'm looking for oh god.\u003C/p>\u003Cp>It's really bothering me, but I will take a moment to find it. What's is it in our docs? In breaking changes 10.6, we dropped support for custom npm modules in the run script operation. Prior to this release, directors relied on VM 2 to run codes in the run script operations and flows. VM 2 is now unmaintained with a critical security issue that could potentially allow for code to escape the sandbox and access the machine, which hosts directors.\u003C/p>\u003Cp>So we've migrated to isolated VM to allow flows to run safely. This basically means you can't use just arbitrary NPM packages in flows anymore. You can still turn them into custom operations, but it isn't isn't here anyway. I learned a lot in during the process of this. There are lots of things you use in JavaScript, which you think are part of JavaScript, things like set interval, things like console log, set time out, fetch, that you just think, oh, this is part of JavaScript, but it's not part of JavaScript.\u003C/p>\u003Cp>It's either part of the browser's implementation of JavaScript or Node JS' implementation of JavaScript, many of which are shared. Isolates don't have this. Isolates just have pure JavaScript functionality. And the I I what I was trying to find was the list of what that meant. I've I can't find the word off the top of my standard objects.\u003C/p>\u003Cp>Boom. Found it. Thank you. So this chapter documents all of JavaScript standard built in objects. So you have access to global this infinity, not a number undefined.\u003C/p>\u003Cp>You have these function properties only. You have some fundamental objects like object function, Boolean symbol, a few error objects, a few number and dates, a couple of text processing functions, a few on arrays, key collection structured data and so on. What you'll notice here is a lack of console logs. What you'll notice here is a lack of date, which is what I was which is what I was going for there. We're not gonna lose any sleep over this.\u003C/p>\u003Cp>I'm literally just gonna we'll remove finished. It just isn't worth the time right now. You know, I do like it though. I wonder if can I add can I add after the fact a date created? Or did that have to be done at the time of Based on my I think it might have had to be done at Yeah.\u003C/p>\u003Cp>I just scroll down. Though. Exactly. And then just flip it over. I'm just taking a look.\u003C/p>\u003Cp>There's a sort field, which is great. That's okay. Take a little take a little cheeky screenshot of this so I can refer back to it. There we go. I'll put that off at the site, and then we'll delete this collection.\u003C/p>\u003Cp>I there might be a way to do it, but I I don't have the the patience. If I see But what I want is date created because I think that would be interesting. That's all. I just wanted that one extra field. So half width.\u003C/p>\u003Cp>So we also had in here a win, which was a boolean. We had turns, which was an int. We had snakes, which was account. It was a number. It was also an int.\u003C/p>\u003Cp>And we had finished, which we've effectively replaced with a date created. So half, half, and whatever. Half. Okay. So but but so they run-in isolates.\u003C/p>\u003Cp>And isolates what it taught me was all these creature comforts I have in JavaScript are not in JavaScript, they're in Node. Js or they're in the browser implementation of JavaScript. But, yeah, that's how I basically learned that.\u003C/p>\u003Cp>Speaker 1: There you go. I had no idea. I had no idea console log was not built into JavaScript. That seems so odd. But I guess the console you're dealing with is in the browser, so it does make sense.\u003C/p>\u003Cp>Speaker 0: Yeah. Let's create a new game. Let's create a rematch, see if we can get it to work. But, yeah, just fascinating. Yeah.\u003C/p>\u003Cp>Isolates. And isolates are not, you know, while that's running, isolates Cloudflare. I wanna say Cloudflare Workers. Yeah. Here.\u003C/p>\u003Cp>They have some really good reference while I was learning more about it. Isolates. VA orchestrates isolates, lightweight context, provide your code with variables that can access in a safe environment to be executed within. You could even consider an isolate a sandbox for your function to run-in. A single runtime can run hundreds of thousands of isolates switching between them.\u003C/p>\u003Cp>And isolates memory is completely isolated, so each piece of code is protected from other untrusted and user written code in the runtime. Islets are also designed to start very quickly. Instead of creating a virtual machine for each function, ISIT is created within an existing environment, blah, blah, blah, blah, blah. So but what's interesting is they are they are way more limited.\u003C/p>\u003Cp>Speaker 1: Yep.\u003C/p>\u003Cp>Speaker 0: Yeah.\u003C/p>\u003Cp>Speaker 1: I wondered I wondered why because I I think what did I I was reading yeah. I've been reading through the documentation, and it said that the the specific JavaScript that you were able to call within within lots of these areas, the directives were limited. And I was like, oh, that's that's an interesting and I figured it was security, but I didn't realize there was so much to learn here. But that is why you come here to Ready, Set, Battlesnake is to learn things about Battlesnake and JavaScript and directives.\u003C/p>\u003Cp>Speaker 0: And it worked.\u003C/p>\u003Cp>Speaker 1: And it worked. Let's go.\u003C/p>\u003Cp>Speaker 0: And it worked.\u003C/p>\u003Cp>Speaker 1: We did it.\u003C/p>\u003Cp>Speaker 0: So what we're gonna do now really is we're gonna just hit create rematch. And if I can I create loads of games in in, like like Yeah? Yeah.\u003C/p>\u003Cp>Speaker 1: Yeah. It's gonna break things.\u003C/p>\u003Cp>Speaker 0: We we we have also not built just a moment. We have not built logic to know which game is happening. We'll do one at a time. Top of them, we have. We have.\u003C/p>\u003Cp>We have. Because every turn is individual on the move endpoint. And then on the start and end, we're referring to the IDs. Actually, we could be running a load of these in parallel.\u003C/p>\u003Cp>Speaker 1: Alright. So give it a try. Let's see what happens. See what happens.\u003C/p>\u003Cp>Speaker 0: What's the worst that could happen?\u003C/p>\u003Cp>Speaker 1: We end up having to pause our episode because we made a mistake and we have to fix it off camera. That is the really the worst that could happen.\u003C/p>\u003Cp>Speaker 0: Oh, I am a bit concerned now because I don't even know when they've completed. That's another problem is I actually don't know when they I've taken a a guess that they won't take longer than each other. Like, alright, that game's over. I mean, let's just see. There you go.\u003C/p>\u003Cp>We're not winning every time. We have a number of turns. We have a number of snakes. Let's, let's just create a few more games. Like, I I want a a reasonable set to start with.\u003C/p>\u003Cp>All of them are gonna start with 2 snakes. Go on.\u003C/p>\u003Cp>Speaker 1: I was gonna say just so if you, like, if you're if you're watching this and you're like, oh, I don't wanna have to, like, do this. It's it's causing my browser. I don't like all those tabs. You can, like, do all this from the CLI too. Like, you can actually go in and just, like, continually create games for the CLI.\u003C/p>\u003Cp>This is just easier than going in and setting up the CLI right now.\u003C/p>\u003Cp>Speaker 0: And I don't wanna set up the CLI in this moment. So we should actually know if any games are outstanding because, yes, see, there's no turns and no wins on this one, which tells us that game is pending and now it has concluded. So, actually, we do have a little indication of how games are going.\u003C/p>\u003Cp>Speaker 1: Look at us.\u003C/p>\u003Cp>Speaker 0: So we'll create a few more games with a mixed player count. Okay. And if we refresh Directus, we should see okay, there are a few games still pending completion. If I refresh, 2 games pending completion, refresh 1 game pending completion, refresh. That game is still going.\u003C/p>\u003Cp>That game is still going. That game is still going.\u003C/p>\u003Cp>Speaker 1: Oh, still going. We're doing well.\u003C/p>\u003Cp>Speaker 0: Is that is that the last one we created?\u003C/p>\u003Cp>Speaker 1: I mean, we'll find out. It must be. I hope it is.\u003C/p>\u003Cp>Speaker 0: Descending. Oh, they've all concluded now. Okay. I think that's good to start. So Yeah.\u003C/p>\u003Cp>Let's go to insights, see see what we can do. Snake battle station. Let's pick a little kiss. Pick wonderful. Pick a little thing here.\u003C/p>\u003Cp>Is there a I don't is there like a snake? No? Is there is I don't want this game. Yeah. Cool.\u003C/p>\u003Cp>And our battlesnake. You know, do you wanna know why green feels like a very snaky color to me?\u003C/p>\u003Cp>Speaker 1: It does. Save. That is\u003C/p>\u003Cp>Speaker 0: the right one. Snake battle station. So this is, director's insights. It's this little module over here. And here, we can create dashboards.\u003C/p>\u003Cp>I don't really use it very much personally, just because my use cases don't demand many dashboards. So there might be a little bit of learning here. You can create panels. Just as a note, this is panels also an extension type. So if you wanna, you know, build on top of these panels, you know, build additional panels, you're more than welcome to.\u003C/p>\u003Cp>For this, I think we're going to stick in here. You know, we have the pie or donut chart, which feels like a good starting point. So we're gonna look at exactly what I had in mind. So win, Win. Count.\u003C/p>\u003Cp>Sure. Don't care about donut. That's just a display thing. Show labels. Yes, please.\u003C/p>\u003Cp>Show legend. Yeah. Pro oh, do you wanna know what? Let let's just see what happens here. Can because I think we can do conditional styles as well.\u003C/p>\u003Cp>So win rate. Okay.\u003C/p>\u003Cp>Speaker 1: Hey. Dang. That was I don't wanna say that was too easy, but that was very smooth.\u003C/p>\u003Cp>Speaker 0: Yeah. Although I we do need a legend. We do need a legend quite desperately.\u003C/p>\u003Cp>Speaker 1: I would just always assume the the bigger one is my win rate. That's really\u003C/p>\u003Cp>Speaker 0: It it you you are correct. Alright. Win rate win no. Win rate feels good. We can do, games, you know, we could do, the x axis is this one.\u003C/p>\u003Cp>Right? So date created maybe and turns, like how well am I doing over time.\u003C/p>\u003Cp>Speaker 1: Yeah. Yeah. I like it.\u003C/p>\u003Cp>Speaker 0: Aggregation. I don't really know what I'm doing here.\u003C/p>\u003Cp>Speaker 1: Well, okay. Just leave it leave it without aggregation and see what happens.\u003C/p>\u003Cp>Speaker 0: Yeah. Sure. And we'll call this one what are we doing here? Turns. Turns feels like a weird one.\u003C/p>\u003Cp>Maybe we just do turns feels weird. Why don't we do, like, account? Because I think what it will do, that's tell\u003C/p>\u003Cp>Speaker 1: us the number of games. Sure. Let's find out. Right?\u003C/p>\u003Cp>Speaker 0: Right. Games over time. Of course, we'll be looking at a very micro Woah. I'm not sure this is gonna pan out. This totally is not correct.\u003C/p>\u003Cp>Okay. Something. We did a bad thing. Aggregation can be forced as well. Right?\u003C/p>\u003Cp>Speaker 1: Can you? Well, set it as false to see what happens. See what that change does. Oh, didn't like that. We are not doing that.\u003C/p>\u003Cp>I guess we do need to have Oh,\u003C/p>\u003Cp>Speaker 0: aggregation is gone. Okay. So we have turns. We have snakes, we have x axis, which is date created. Do we need something on both axis?\u003C/p>\u003Cp>I'm sure. Do we? Oh, maybe not. I mean, it kind of looks like we do. I mean, I'm kind of as you can tell, I really don't make a lot of dashboards because, you know, it's just not it's just not my world.\u003C/p>\u003Cp>Maybe if we let's let's let's, see what else we can do. We have I like metric firstly, which is like total games played. That one feels easy.\u003C/p>\u003Cp>Speaker 1: Yeah. That's simple.\u003C/p>\u003Cp>Speaker 0: Aggregate function, just count. Count. Yep. And, suffix, Games played. Star decimal unit.\u003C/p>\u003Cp>Notations standard. Yeah. Yeah.\u003C/p>\u003Cp>Speaker 1: Oh. Uh-oh. GraphQL validation error?\u003C/p>\u003Cp>Speaker 0: I think I'm just not I'm genuinely thinking I'm just not selecting enough field. Do we need to do a field ID? Yeah. We did. Oh, there we go.\u003C/p>\u003Cp>15, 15 groups load. Nice. Sure. Okay. It's not pleasing my eyes very much that, but let's, keep taking a look.\u003C/p>\u003Cp>Speaker 1: Yeah. What do we have still?\u003C/p>\u003Cp>Speaker 0: Font size auto, which makes sense, or you can make it fixed and then you can add padding accordingly. I do get the auto. Auto makes a ton of sense. We have a line chart. We have time series.\u003C/p>\u003Cp>So the collection, once again, games, the group aggregation, oh, group precision minute because we set a few games off at the same time. Count, date field, date created, date range, past 15 minutes. Sure. Value field. I, turns.\u003C/p>\u003Cp>Snakes. Oh, I don't know. I mean, can I edit can I can I oh, there it was? Can I clear the value, please? I'm just gonna hit tick.\u003C/p>\u003Cp>Alright. Didn't do anything. I've picked I've picked the wrong combination of picked the wrong\u003C/p>\u003Cp>Speaker 1: combination of options. We almost we almost did it.\u003C/p>\u003Cp>Speaker 0: Did you see how confident I went in?\u003C/p>\u003Cp>Speaker 1: You went in. Listen, confidence is half the game here. We just gotta go in thinking you know what you're doing and, eventually,\u003C/p>\u003Cp>Speaker 0: you know I'm not really sure. I'm not really showing this bit of director stuff pretty well.\u003C/p>\u003Cp>Speaker 1: No. I think it's I think you're doing this fantastic. I like that we've been able to get stuff like we so it's because I said it was so smooth. Hey. There we go.\u003C/p>\u003Cp>Nice. And these are showing us?\u003C/p>\u003Cp>Speaker 0: So this is showing us the number of turns taken on a minutely basis. And that was where I set loads of games off, and this was just as it was coming down from the last data point that was held. If I do it the past 30 minutes, I think I'll get a slightly oh, maybe not.\u003C/p>\u003Cp>Speaker 1: It's a good try.\u003C/p>\u003Cp>Speaker 0: Yeah. This is oh, here we are. Automatic based on data. That feels good. Okay.\u003C/p>\u003Cp>So 80 turns over here and then 320 turns over here. And then if we create a couple more games now, we'll create a game.\u003C/p>\u003Cp>Speaker 1: Yeah. And I do like this. Like, if you were actually looking at this over a period of time, this would make a lot more sense.\u003C/p>\u003Cp>Speaker 0: Could be really insightful. Yeah. So let me just save that, and we'll just make sure. Oh, thank you. Woah.\u003C/p>\u003Cp>Last game has not concluded yet, so we'll just let that conclude. Okay. So if we go back to insights now, there you go. It's a little bit more of a of a view. Right?\u003C/p>\u003Cp>So I don't really wanna look at the number of turns though. I wanna look at is it count? I think that will give us the same graph. Yeah. What I want is I just wanna know how many there are.\u003C/p>\u003Cp>Speaker 1: How many games?\u003C/p>\u003Cp>Speaker 0: How many how many games? Do you think turns is interesting?\u003C/p>\u003Cp>Speaker 1: I no. I mean, I think let's I so turns is interesting, but only if you're playing 1 at a time.\u003C/p>\u003Cp>Speaker 0: Exactly. And I think I think I think there's a way to do that. I've seen other people do it. So we had I think it might this global variable variable key can be like game. Confidence.\u003C/p>\u003Cp>Speaker 1: Lots of confidence.\u003C/p>\u003Cp>Speaker 0: Game interface. That's not what I want. Type. Is it in here? No.\u003C/p>\u003Cp>I've seen it. I've seen it somewhere else. Is it maybe called global relational and you pick a collection, you pick games, display template? Yes. You find the ID.\u003C/p>\u003Cp>I think this is it. So you select a game. So it says select an item. So can I, whatever? But we we won't look at editing that.\u003C/p>\u003Cp>So you select a game, right, like this. Great. And then you can use that global variable in all of the other boards Okay. In all of the other panels. So if I you know, maybe we do this.\u003C/p>\u003Cp>This is like global values up here. Great. And then we have this, you know, dynamic portion. So what do we want to know here? Well, we want to know I I don't know.\u003C/p>\u003Cp>Like, what about are we doing we're not doing per turns. We're not storing turns too. Maybe we just do what do you think?\u003C/p>\u003Cp>Speaker 1: There's only one other thing that I thought would be really fun, but I think that the logic is gonna take too long. No. And it I mean, not gonna be related to this panel. I think it's I I'd be very interested to pull in some custom that that the the actual gift for the game. I think there's something interesting there to be able to pull in.\u003C/p>\u003Cp>Speaker 0: I think we could do that. I think we could do that. Yeah.\u003C/p>\u003Cp>Speaker 1: That's an ambitious it's an ambitious goal. You wanna try?\u003C/p>\u003Cp>Speaker 0: Yeah. Go on then. Right. Tell me how to get the tell me how to get the URL. Go to your game.\u003C/p>\u003Cp>I'm I'm going. Got it. Alright. Go to view\u003C/p>\u003Cp>Speaker 1: as GIF, and then it'll give you the format\u003C/p>\u003Cp>Speaker 0: that you I will only click it when you say it correctly.\u003C/p>\u003Cp>Speaker 1: GIF\u003C/p>\u003Cp>Speaker 0: GIF. You may. GIF. Okay. And there's a URL and that's the game ID.\u003C/p>\u003Cp>Speaker 1: Grab that,\u003C/p>\u003Cp>Speaker 0: And that's that ID is movable. Right? Like, that ID is the ID of the game.\u003C/p>\u003Cp>Speaker 1: So\u003C/p>\u003Cp>Speaker 0: you can buy back\u003C/p>\u003Cp>Speaker 1: and game by game.\u003C/p>\u003Cp>Speaker 0: Yeah. So let's try our hand.\u003C/p>\u003Cp>Speaker 1: We need to do a custom panel.\u003C/p>\u003Cp>Speaker 0: That is that is definitely what we need to do. Yeah. But, right. So what are we gonna do here? We're gonna go up one layer.\u003C/p>\u003Cp>We're gonna go MPX create directus extension at latest. Whoops. Extension. Helps when you type it correctly. Right.\u003C/p>\u003Cp>What we're gonna do, we are oh, you're you're putting me on the spot here. I'm not overly confident. And viewers, if we don't get it done, we're not gonna get it done. But let's call let's consider this a stretch goal. I also haven't used these global variable values too, too much.\u003C/p>\u003Cp>While that's kind of doing its thing whoops. Docs, Data Studio app. I think it might be over in the user guide, insights, panels, global relational value here. Oh, and then in other in other, ones, you can use the variable key like in other panels. That's interesting.\u003C/p>\u003Cp>Anyway, we want a panel, director's extension, snake panel. JavaScript install dependencies. Okeydokey.\u003C/p>\u003Cp>Speaker 1: I'm glad I gave you this challenge, and I am not the one having to implement it. This seems right. This seems good.\u003C/p>\u003Cp>Speaker 0: It all seems good with the world. Alright. So we have the panel here. We have our panel dot view. It has been a while since I built an extension.\u003C/p>\u003Cp>So here we have all of the configs. We'll call this snake GIF snake GIF. Sure. We could do loads of configuration. We're not gonna do that.\u003C/p>\u003Cp>First thing we're gonna test is if we just have a if we just have a string like we manually insert it to start, can we can we play? Can we play it back? So in the panel, we have this text here, and I think all we would need to do, there's a bit of an assumption in here, is we're gonna paste in this URL and we're going to interpolate here the text, right? That feels right?\u003C/p>\u003Cp>Speaker 1: It feels very good.\u003C/p>\u003Cp>Speaker 0: Npm run dev. Sorry. Npm. I'm I'm not even in the Npm run build. I wasn't right running the right command or in the right location.\u003C/p>\u003Cp>And then we will I think what I'll do is I'll can I hang on? Kinda wanna open them side by side. There we go. And then I wanna just kill that, restart it. Once again, fixed as of, like, yesterday's release.\u003C/p>\u003Cp>I just haven't got around to upgrading this one yet. Open up our panel, and I'm hoping snake GIF. Hey. We insert an ID here. Let's go get an ID.\u003C/p>\u003Cp>This one, 72 turns. And so I I can't believe it might actually why did I say that?\u003C/p>\u003Cp>Speaker 1: You've jinxed it now,\u003C/p>\u003Cp>Speaker 0: but it's okay. Here. Hit save. Okay. Save.\u003C/p>\u003Cp>Speaker 1: And\u003C/p>\u003Cp>Speaker 0: oh. Don't know, mate.\u003C/p>\u003Cp>Speaker 1: Happening. Oh,\u003C/p>\u003Cp>Speaker 0: I have my dev tools kind of detached. Content security policy.\u003C/p>\u003Cp>Speaker 1: So this might actually be cross it might be course with the dev side\u003C/p>\u003Cp>Speaker 0: platform. Which is which is, the ilk of what this is. How do I solve it? How do I solve it? It's a GIF.\u003C/p>\u003Cp>It's a GIF at a location. Can we import it maybe? Can we import it? And then how would we import it? Let's take care of let's let's consult the docs.\u003C/p>\u003Cp>So we have in here extensions, developing extensions, extension services, accessing files, import a file. This is inside of an API extension how we would import a file. We would instantiate a file service. We have an asset key, which has the URL and a file object, which contains additional metadata, and then we read it in order to to return it. Excuse me.\u003C/p>\u003Cp>So let's have a think about the right way to play this. We could press a button. It'll import it and then play it back. It's a good idea. Let's try\u003C/p>\u003Cp>Speaker 1: that. That seems easy. Let's\u003C/p>\u003Cp>Speaker 0: try that.\u003C/p>\u003Cp>Speaker 1: I say this. I say this.\u003C/p>\u003Cp>Speaker 0: Easy as a That sounds easy. That sounds easy.\u003C/p>\u003Cp>Speaker 1: Kevin problem. Sounds like an Android watch, and Kevin solves this tactical challenge.\u003C/p>\u003Cp>Speaker 0: Alright. Alright. Alright. So let's just keep this easy for a moment. Let's let's stitch this off, and let's create a new bundle.\u003C/p>\u003Cp>And I'll call this one directus extension, and I'll just call it, Battlesnake Viewer and install dependencies. So that's creating a bundle. In that bundle, we'll have an endpoint and we will have, and we'll have an endpoint and a and a panel. So we'll CD into the battlesnake viewer, and we'll go npm run add. And the first thing we'll add is actually the endpoint.\u003C/p>\u003Cp>We can just call this one importer. JavaScript. And now inside of Battlesnake Viewer, source importer index dot j. So this is again, our custom route as mentioned earlier. Now inside of our existing one, we've actually got this is snake GIF.\u003C/p>\u003Cp>We have our actual snake here. We're gonna we're gonna just kind of nab this structure for a moment, inside of the importer here. We'll remove these actual things here, like so. Okay. So we'll call this one importer.\u003C/p>\u003Cp>Right. So when we hit importer, what we're gonna do, and we're gonna do a post request here, is we are going to access files. I'm gonna copy all of this. Here. We'll see what we can do.\u003C/p>\u003Cp>Oops. Okay. Router dot post. We will create a files service. Now I actually think, the docs do talk about importing that.\u003C/p>\u003Cp>So let's just take a little look at the top here. Yes. Okay. So in the endpoint itself, we have a router and a context. Awesome.\u003C/p>\u003Cp>Let's do a let's do a little more side by side jobby here. Right. So we have router and we have context. And up here, we're going to grab these values out. Then inside of router.post/cool, we will create a new file service.\u003C/p>\u003Cp>We will look at rec. So file.url is going to be req.body. URL. Cool. And the file object itself, how much of this must we pass?\u003C/p>\u003Cp>Let's look at the file object. API reference files. File object looks like this. How much of this must we pass? I don't think any.\u003C/p>\u003Cp>I don't think we need any of this. So I think we'll remove that. So we have our URL. Then we will import it. It will come back.\u003C/p>\u003Cp>We will then have the asset key, and we'll return the data. So if all goes well, npm run build, This is our importer, docker compose up. And I think what we wanna do here is test whether this endpoint works. Now I still have ngrok running. I don't still have it running.\u003C/p>\u003Cp>I've been running it since the beginning of this, this session. And I have hopscotch here, which is similar to Postman. It's just like an HTTP Explorer. So here, if I go importer post and inside of the let's just hit send there. Okay.\u003C/p>\u003Cp>Timing out, timing out, timing out. There is an error, invalid URL, input object object. Okay. Let's just cancel that. Let's set a body.\u003C/p>\u003Cp>Let's set this to be application JSON. There we are. And what we want in here is a URL of and then this exporter battle sync. So hopefully this will return the ID of a new item in our directors project. Okay?\u003C/p>\u003Cp>It's still going. So not feeling good about that. Invalid URL. Is it, though?\u003C/p>\u003Cp>Speaker 1: Is it? Apparent I mean, no.\u003C/p>\u003Cp>Speaker 0: No. No. Object object. Import 1. So it was struggling with import 1.\u003C/p>\u003Cp>So let's let's take another look at this. So it was struggling here with the file service dot import 1. Req dot body dot URL. Now to be fair, we never once took a moment to check whether it was gonna be req dot body. So let's just res.sendokay for a moment and that's console.log req.body.\u003C/p>\u003Cp>There was a bit of an assumption in there. So we will rebuild that and then we will restart that. Okay. And we will send that and we should hopefully get an okay back. We did.\u003C/p>\u003Cp>So let's take a look. So inside of req.body, yeah, dot URL. What's going on? Is this a URL we can hit? Oops.\u003C/p>\u003Cp>Yeah. Should be. Yeah. Sure. And let's just test that there's nothing funny going on with, like, access here.\u003C/p>\u003Cp>If I go to our file library and import, this is the RF for importing and hit import. The GIF is there. So there is nothing wrong with that. So we're just doing that via API instead. So fair enough.\u003C/p>\u003Cp>Fair enough. So we have the URL. We have some accountability, which I think we might just be able to bundle some\u003C/p>\u003Cp>Speaker 1: for now.\u003C/p>\u003Cp>Speaker 0: Get rid of it for now. Let's do npm run build and restart the container. Go to hopscotch and send this.\u003C/p>\u003Cp>Speaker 1: Okay. Great.\u003C/p>\u003Cp>Speaker 0: And this time, we've got a URL, which means there was nothing wrong. So let's just do one step at a time. That's console dot log the asset key. Yep. Rebuild.\u003C/p>\u003Cp>I'm determined to get this to work now.\u003C/p>\u003Cp>Speaker 1: We are gonna do it. We have we have the Cool. Technology.\u003C/p>\u003Cp>Speaker 0: We have the ability. That should still just return an okay, but it hasn't. Why? Code error invalid URL. It gives me it gives me it gives me the docs might not be right, which is possible.\u003C/p>\u003Cp>So as much as I wanna just be like, but that's what it says to do. Let's take a moment and let's actually, if we go to accessing if if let's find let's find the docs for this and let's just step through because all of the services are just in the code base. It's not my preferred way, of course, to be looking at stuff. Let's take let's take a little look. What we care about here is import 1, and it does expect an import URL and a body partial.\u003C/p>\u003Cp>So maybe maybe we did need to pass in, could not fetch from URL. The error said invalid URL out of interest. So maybe yeah. Because what it does is it grabs the raw file and then uploads it. Maybe we need to we must pass in this body.\u003C/p>\u003Cp>Just a thought. And that was the partial body objects. And maybe I mean, that's a partial body object. Maybe there's something else we must pass in this at the time of creation. Let's, let's hit send.\u003C/p>\u003Cp>Speaker 1: Come on. Okay. Okay. Okay.\u003C/p>\u003Cp>Speaker 0: In fact, again, it's taken that long. Yeah. No. Input object object code invalid URL. That invalid URL.\u003C/p>\u003Cp>Couldn't fetch file from URL object object. Isn't that interesting? Because the URL to me is a is a string. Right? Yep.\u003C/p>\u003Cp>And found that URL. But this time, before all of this, I logged service external file is unavailable. Couldn't fetch file from URL object object, but it isn't. It is a string, and this is the string. Fascinating.\u003C/p>\u003Cp>I mean, let let's just, you know, I've been a bit cheeky here. Maybe we, go back to accessing files, and I do actually create it with this accountability object here. Yep. I'll take that. I'm pretty sure I know it's not needed.\u003C/p>\u003Cp>I know it's not needed. But that the error isn't saying something else is missing. You've triggered an unhandled rejection. You may okay. Yeah.\u003C/p>\u003Cp>Fine. Okay. True. So if we go if we try around this, right, and then we catch an error. We console dot log the error.\u003C/p>\u003Cp>And we send this.\u003C/p>\u003Cp>Speaker 1: And\u003C/p>\u003Cp>Speaker 0: We are expecting an error, which is fine. We've got 503 service unavailable. Why? Couldn't fetch file from URL. This is what's troubling me.\u003C/p>\u003Cp>Object object. Let's take a let's just take another look here. So import 1. I think I worked it out.\u003C/p>\u003Cp>Speaker 1: No. That was quick. Goodness.\u003C/p>\u003Cp>Speaker 0: It is an opt I did pass in an object, didn't I? But that's not what's being passed in in the in the reference. The import URL is a string and the body, which is a partial file that should have been passed in as a string. All you need to do, Kevin, is read. Read the docs.\u003C/p>\u003Cp>So read\u003C/p>\u003Cp>Speaker 1: the There's another person here, and we still didn't get there.\u003C/p>\u003Cp>Speaker 0: Yeah. Okay. Asset key. We got an asset key that time. Success.\u003C/p>\u003Cp>There should now be a file in the file library. Right? Correct. So we are loading it in. We are loading it in.\u003C/p>\u003Cp>Title, description, tags, file name disk, all grand, but\u003C/p>\u003Cp>Speaker 1: Beautiful.\u003C/p>\u003Cp>Speaker 0: What we also want to do is this. We want to, YOLO. I can't be bothered with all this. We'll just there we are. We'll take the asset key, then we'll read the new asset, and we'll return the data.\u003C/p>\u003Cp>So what we'll get back in in response to this is it will always import. So that's not ideal because if it already exists, we don't necessarily want to reimport it every time. But for the sake of this little demo, I think it's fine. Then we'll get the ID back. So now if I rebuild this and we run this again oh, it was still spinning up.\u003C/p>\u003Cp>What we should get back is the new file object indeed, including the ID. And the nice thing about this is, if I go to access control, public, and I turn on directors files actually, I don't even need to do that. If I go to httpsphzedm.ngrok.io, right, slash assets slash ID will get a you don't have permission to access this error. Right? But what I can do is go to the user, generate a token.\u003C/p>\u003Cp>Speaker 1: Okay.\u003C/p>\u003Cp>Speaker 0: Generate the token, save it, and I pin that to the URL. Nice. Access token equals. And there's our Hey. Merge directly, which is pretty which is pretty cool.\u003C/p>\u003Cp>That's very cool. To keep this easier, though, I'm actually going to change the permissions of the files collection. So then you net we never need to pass in, pass in the key. So public system collections, directors files, all access on. Great.\u003C/p>\u003Cp>So now that's public. So now we've done the endpoint. The job is to create the panel. So what we'll do here is npm run add.\u003C/p>\u003Cp>Speaker 1: That should be pretty easy, I think. I say that. That's a dangerous\u003C/p>\u003Cp>Speaker 0: Easy for you to say.\u003C/p>\u003Cp>Speaker 1: He does it. I know. It's very easy for I I think you've got this, Kevin. I trust I believe. I trust in Bodie.\u003C/p>\u003Cp>Next week, when I'm when I'm driving again, things are gonna go terribly awry, and you're just gonna kick back and and relax and\u003C/p>\u003Cp>Speaker 0: enjoy the experience. Oh, yeah. I know. Viewer. What did we call the previous attempt to an extension here?\u003C/p>\u003Cp>Snake GIF, find viewer, viewer. This is my custom panel. Now there is some text. Now what we need to do, I think what we'll do is we'll remove the options entirely, right, because we don't want any options. It'll be a standard panel.\u003C/p>\u003Cp>And inside of that panel, we are going to have button button. We no longer have text. That's not a prop that's being passed in anymore. We just deleted it. So what we'll do is we'll do a v button.\u003C/p>\u003Cp>No. We won't. We'll do a v let's talk about what all these v things are for a moment. Back in our docs here, back in our docs, we have, resources for building extensions, components. And we have this components playground.\u003C/p>\u003Cp>These are all of the directors components that are inside of the Data Studio are made available to extensions. So we have here v button, and this is the director's button inside of the director's app. So we we have an immediate availability of this v button component, which is, yeah, pretty, pretty nice. So we can access that straight away. So we can go v.\u003C/p>\u003Cp>I did just notice it was v button like this and we'll call this, not v. I keep going back to the button. Sorry. We're looking for\u003C/p>\u003Cp>Speaker 1: No. That was right.\u003C/p>\u003Cp>Speaker 0: No. Yeah. But we want the input because we want to put in the ID. Yes. Yes.\u003C/p>\u003Cp>Yes. Because we'll keep it easy. We'll we'll put, like, right now, manual ID of a game hit go\u003C/p>\u003Cp>Speaker 1: Yeah.\u003C/p>\u003Cp>Speaker 0: Rather than, Automating it. Yes. Pick pick it from a drop down because we're running we're running out of time. So v input to v model. So, what are we gonna do here?\u003C/p>\u003Cp>Export default. We're gonna do data return, and we'll call this ID. Like so. Then inside of here, we'll put in the v input with a model of ID. Then we'll say on key up dot enter fetch, game, GIF, get GIF, fetch, git.\u003C/p>\u003Cp>Cool. And we will add and this is not really an ideal way of of writing these, of course, but methods and we'll call this fetch GIF. Yeah. And we'll just console dot log or you know what? We'll alert with the this dot ID value.\u003C/p>\u003Cp>That's what we'll do for now just to test all of that works before we go too far. So we'll go in here. Oh, I need to sorry. Npm run build was the command there. Oh, error.\u003C/p>\u003Cp>Speaker 1: Come on. What are you doing, error?\u003C/p>\u003Cp>Speaker 0: What are you doing? After component panel component, we're missing a comma. Okay. NPM run build. Lovely.\u003C/p>\u003Cp>And we'll restart that. Also lovely. So let's refresh. Let's refresh. Just took a hot minute there.\u003C/p>\u003Cp>Insights. New panel. Viewer. No options available. Right?\u003C/p>\u003Cp>Because nothing happens to the panel itself. This, wonderful, is our panel and we'll say we'll save it. Hey, enter. Great. So this means we are able to do something with that.\u003C/p>\u003Cp>What we're gonna do with it is we're going to go back to our docs. We're gonna look at our composables for app extensions, which is what this is. I'm gonna just close out everything else for now. And we're gonna use API. And what this will do is it will allow us to make API calls, to our endpoints that are already authenticated.\u003C/p>\u003Cp>Though, to be fair, I believe this is a completely public endpoint, so it shouldn't matter much. But still, we'll do it as as it should be done. So what we're gonna do here is we are going to import use API. We are going to use this composable, like so, and then we can make our endpoints like and then we can make our calls like this. So we will make this async.\u003C/p>\u003Cp>And what we're gonna do is we're gonna post to slash importer/importer. And we have response dot data here. So all we're going to do is console dot log response dot data. Let's build. And this is all basically to get around that that security issue.\u003C/p>\u003Cp>Speaker 1: Yeah. That course issue. I love that there's always just like a 6 step workaround to deal with course. Thank you.\u003C/p>\u003Cp>Speaker 0: As opposed to just dealing with it head on. Yeah. So, we refreshed it. This is great. What's happened to my text box?\u003C/p>\u003Cp>What's happened to the text box? Is it no, but it's not. Where's my text box? Also, what's happened to this one? What's happening?\u003C/p>\u003Cp>Not seeing any errors. The API could not be found. Couldn't load extensions. Okeydokey. Have I missed a step?\u003C/p>\u003Cp>Have I rushed it again? Have I rushed a little bit? Oh, script setup. So we'll be using, we have to move this to script setup. And let's just take a quick look at how this I need to refactor this.\u003C/p>\u003Cp>Of course, we do, but that is fine. Async fetch GIF should just be able to, for the most part, come up here. We'll say, const fetch GIF equals a sync function like so. Then is that legit? Yeah.\u003C/p>\u003Cp>That is legit. And then so that's the methods. Then we have the data, which is the ID. So we will be import import ref from view. We'll create a new variable for that.\u003C/p>\u003Cp>We'll go const ID equals ref. Great. So we've handled that. Then we have props. And I believe the way to do props here is we say define props.\u003C/p>\u003Cp>Can we do this? I think we can do this. And all we care about here is show header, which to be honest, I do not care about. In theory, I've just refactored all of that. Let's take one more quick look, fetch GIF.\u003C/p>\u003Cp>So we have this dot ID. So we don't need that anymore. It'd be ID dot value, but we're not gonna alert it anyway. Kevin, watching\u003C/p>\u003Cp>Speaker 1: your work is just fantastic, by the way. I love that I gave you this challenge and, like, everything has gone wrong. Okay.\u003C/p>\u003Cp>Speaker 0: This is this is an Axios instance. I don't quite remember how it bloody works. So I think what we do here is we do, and I need to check, axios post. Oh, we do we literally just pass in the whole object as the second. Great.\u003C/p>\u003Cp>So in here, we're gonna do URL equals ID dot value. Okay. And then we have response dot data. Cool. So we'll console mock that.\u003C/p>\u003Cp>Alright. Let's rebuild that. Is it gonna complain this time? No, it bloody isn't. Docker Compose up.\u003C/p>\u003Cp>You give me a right workout here. Let's refresh, and we should now have access to use API. Now none of it's loading. Now it is. And there's our text box.\u003C/p>\u003Cp>Let's open up our dev tools. Of course, it didn't like the URL that was before. That was this panel here. Just testing that out. So in here Can I grab a screen?\u003C/p>\u003Cp>Pop it off at the side. Over here, yeah, we need a game ID. So we can do that here. Game ID. So we're not done yet because we're not gonna display it yet.\u003C/p>\u003Cp>If we hit enter, what's happened?\u003C/p>\u003Cp>Speaker 1: Refused refused to load the images.\u003C/p>\u003Cp>Speaker 0: No. No. No. That that's the old one. That's the old one.\u003C/p>\u003Cp>In fact, if I just remove that whole thing, we'll be better off for it. Let's refresh. See no errors. Let's paste that and let's hit enter. Nothing's happening.\u003C/p>\u003Cp>Why? Fetch GIF. Let's make sure this is ever running. You know what? This is all cute and all this.\u003C/p>\u003Cp>Let's just, just in case it's some silly async fetch gift, just in case it's some silly, async function, just in case it's something silly in the way that oh, I think it might have said invalid URL. I think it might. Do you wanna know why invalid URL? Because it isn't the full URL. We're just posting the ID.\u003C/p>\u003Cp>Oh, that's right. Yes. So what we'll do on the importer package is we're actually going to, we're gonna import req.body.url. Cool. So we're gonna make URL.\u003C/p>\u003Cp>Where is it? Here. Exported.battlesnake.comporter.battlesnake.games. And then here, req.body.idgamegame, maybe we call it. And in here, we pass in the earth.\u003C/p>\u003Cp>So now it needs a game. That's the value we need to pass going back to our panel. This is going to be game ID dot value. Let's rebuild the extension. Okay.\u003C/p>\u003Cp>Oh, yeah. We'll refresh. I do understand our network error showing there. Now we'll paste in the ID of the game. We'll hit enter.\u003C/p>\u003Cp>Oh, you know what we didn't do? We didn't open up dev tools. Let's skip to console. Wicked. Okay.\u003C/p>\u003Cp>We now have an ID of a directus I like, a directus asset in the ID. So all we need is the ID here. So this is the ID of the game, game ID, game ID, and then an image in here and the source of that image is going to be, how is this going to work? We need to just have a quick think about this. I'm gonna try something out.\u003C/p>\u003Cp>I think if we go /assets slash and then we need a value in here. So in here, we're gonna go image ID, and it's gonna be a ref and image ID dot value equals response dot data dot ID. Right? So Yep. Now this has the value in it of the ID, image ID.\u003C/p>\u003Cp>First thing we'll do here is just a quick v if image ID. We only wanna show it when that's the ID, and then here is going to be image ID. Let's see if that works. I'm not a 100% sure it will, but should do.\u003C/p>\u003Cp>Speaker 1: I have faith oh, yep. There we go.\u003C/p>\u003Cp>Speaker 0: I don't. Let's refresh.\u003C/p>\u003Cp>Speaker 1: 1 of us has got 2.\u003C/p>\u003Cp>Speaker 0: Great. ID is not defined. Why? Because ID dot value is gonna be game ID dot value. I'm feeling the heat, but I'm having a blast.\u003C/p>\u003Cp>Speaker 1: I know. I was gonna say I feel like I gave you, like, the small challenge that became the much larger Yeah.\u003C/p>\u003Cp>Speaker 0: But not not 145 in or whenever it was. That's not really the time to do it. But if we wait just a moment, nothing happens. Why? Console.\u003C/p>\u003Cp>Assets undefined 4 3. Fine. Let's figure out oh, image ID. Complete typo there.\u003C/p>\u003Cp>Speaker 1: Remember this, Kevin. Remember this as the challenge comes next week.\u003C/p>\u003Cp>Speaker 0: I'm having I'm having a blast. I really enjoyed last week, last episode. And now\u003C/p>\u003Cp>Speaker 1: we have Hey. What is I? Kevin. Kevin. Kevin.\u003C/p>\u003Cp>Kevin.\u003C/p>\u003Cp>Speaker 0: In an ideal world, you can obviously select from your existing games. It will handle all the logic for you. I think we're firmly out of time to do that this time around. Next time we and next time we won't be touching insights. But everyone, I hope you've had a wicked time.\u003C/p>\u003Cp>Any final remarks, Andrew?\u003C/p>\u003Cp>Speaker 1: Thank you, Kevin, for taking on the challenge. Thank you all for watching. And be sure to tune in for our next episode of\u003C/p>\u003Cp>Speaker 0: Ready.\u003C/p>\u003Cp>Speaker 1: Ready. Set. Set. Battlesnake. Battlesnake.\u003C/p>\u003Cp>Speaker 0: Nat, when you edit this, can you just time it so we get that right? Thank you. Bye.\u003C/p>","Hello? Oh, were were you waiting to do the thing? Alright. We can I got you excited? I got you excited. Hello, and welcome to ready Ready. Set. Battlesnake. We are running out of time to nail it, and I I have to be honest. My my, my confidence is starting to wane that we're gonna get it. But hello, everyone. Welcome to episode 3 of 4 for Ready, Set, Battlesnake. My name is Kevin. I work at Directus and My name is Andrew, and I work at Devcycle. Absolutely. And today, we're gonna pick up right where we left off in building a Battlesnake with, Directus and in the next episode with Devcycle. We've both been involved in the Battlesnake project for some time in various ilks. And honestly, we're we're just having a great time, if I may if I may talk for us both. I'm with you. I think this has just been so fun. I haven't dug into Battlesnake in such a long time that this was such an awesome thing to come back into, especially oh, look at that. My light turned off. Especially after, especially after just getting into the the nitty gritty of Directus over the past, like, couple months, getting ready to build some of this stuff out. So, yeah, this has just been it's been so much fun, and I'm excited that we're only halfway through right now. Like, we've got 2 full sessions left, and I think it's gonna be amazing. So am I. So let's recap where we were at just at a high level. So we have a local directors instance running. Inside of that instance, we have a custom endpoint extension. We have a custom endpoint extension because we need to know with confidence that, a snake can exist having a root endpoint and a slash move endpoint at the very least. And the webhook, the webhook triggers for flows can't guarantee that you get a random URL and we need we need both of those relative past. So we created an endpoint and all the endpoint does is it fires off this flow. There's one for the info of our snake here, which gives the information about our snake. Is the font size okay? I can bump it up if necessary. It's okay. I mean, bump it up a little bit. I feel like it's not bad for folks that are watching. Yeah. Yeah. It's good. Yeah. Yeah. Yeah. Yeah. And then our move endpoint, which is what we spent most of our time working on in the last episode. So request comes in with a full board state. We set up some global information from that state. We grab is move safe, up, down, left, right, or true. We grab snakes, board width, board height, my head, and you kind of all of these conveniences. Then we run avoid walls, and we see whether whether the wall's gonna be hit. But what you'll notice is at the top is that we pull in is move safe. We do all the work to it and then all we do is return is move safe. So we're moving this object down the chain kind of manipulating it at each step because that's ultimately all we need. We avoid the walls. We avoid all bodies of all snakes once again, starting with is move safe, ending with is move safe. Then we avoid head to head. This is what we spent most of last session doing once we got the flow running. It might not be the most elegant, but what we do is we look one space around every snake that isn't us, and we avoid those spaces too. Once again, we start with this move safe coming in, and we end with this move safe going out. And finally I love we determine I love that you're saying this is not elegant, Kevin. Like, this is like yours is so much more elegant than mine. As you're gonna find out next week when I jump back in, week 1 week 4 are Andrew, learn learn with Andrew. Specials. Andrew specials. Exactly. So this is such clean code. I love it. Oh, well, thank you very much. I'm sure some of our viewers will disagree and I won't stop them. And then and then finally, we grab that array of is move safe. We basically just pick 1 at random that is still available and we return that as an object move next move, which is either up, down, left or right, and we continue on. And so in this episode, I think we were going to start playing around with not necessarily adding more logic to the snake. I think it's a pretty robust snake right now, but instead, we're gonna start saving game data to this director's project, and then perhaps we'll do something with the insights module. How does that sound? That sounds fantastic, Kevin. That sounds like a great way to spend an afternoon for me slash Wonderful. And afternoon, no? It is now but I realized that people are watching this from everywhere. So maybe it's from morning. Everywhere. Technically, it's it's, we've got afternoon for me, evening for Kevin, and morning for one of our wonderful viewers out. I'm sure somebody is more important for you. Good good morning from 9:30 at night. Okay. So so so I've already had to think about how how this should probably shape up, which is this return move this return move operation here does some work, right? It picks the move and then it returns it. I think we should decouple these. So we should do one final step which picks a move and then the final step which just returns the move. And then we can put in like an inter we could put in one additional operation in which will store data in a database. I think that's probably the, you know, we have to decouple it and then we can we can take in the entire board state from the trigger, store that and we can store the move that we ended up concluding before we send it on. That's that's what I think we should probably do. There is a question of what we want to store. You know, we also have this additional start and end move point. I think end always returns the final state of the board. So there's something to be to be said about that as well. Yeah. What do we what do we want to store? Maybe we start with we we just store the the move. Like, we start really simple. We just say what is the final move we chose? Yeah. I think that's probably easy number 1. I was also gonna say we could record win or loss, like, if we wanna go really simple. But technically, I think move is probably simpler because then otherwise, we're gonna have to parse through the JSON data that we get back. So yeah. Let's do let's do move and then maybe, like status of the game and, and then go from there. Interesting. Let's just let's just go till we can't go anymore. Now just as a note, there is a limitation that we have to work within with directors. Now we actually just did a different directors TV episode on this. It was request review, filtering JSON objects, I think it was called. But, basically, if we store a JSON blob inside of a field in directors, we can't filter inside of that blob. It is just a string. So what we'll need to do is extract specific values and store those as individual values if we want to use them for insights. We just have to be aware of that. Let's let's start by doing what we said we would do, which is adding this extra, splitting this out into 2, and then we can figure out how we're gonna store data. I think that's probably the safest bet. So, let's add a new operation here. Let's call this 1, determine move. I think that feels okay. I think we'll give it the key of move. And I don't know why I've started doing that without having copied out this. So determine move, and this will be a run script. You can copy and paste that in here, except all we're gonna return now is move next. I think all we need to return is the strength next move. That's all this one has to do. And then we're just gonna re kind of configure these. So this one's gonna go here. This one's gonna go here and we hit save. And this keeps happening. Don't know why, but it seems to work if you just kind of what was it? Do it again? Yeah. Because that's what it was. Disconnect it and then save and it should work. Yeah. Like this. We do. Well, we did that. Okay. Cool. Cool. Cool. Cool. Yes. And now we're And then, which feels odd. Now today, there was actually a release of Directus the day that we are recording this, but I'm not feeling brave enough just just in case. Just saying it. So this this may well have been fixed. Do we remember what it was? Because it ain't ain't Don't say these. Bloody words. Is it not working? See. Hang on. Hang on. Okay. What if we go this way? Yeah. Okay. Fine. And now switch it. I think we had to, like, maybe. No. What the heck? This this resolved with this last time. So this is Yeah. But nothing nothing's changed, Andrew. Nothing's changed. Okay. Go back, and, like, remove it from there, save, and then go back, go out, and come back in. You think? I don't think that's what we did, but I wonder if it will No. No. We we definitely didn't do that. We did this and then this and then This is why you should always watch your VOD recording if you're trying to bug some of the buttons. We fixed it. Watch your VOD recording. We did. Oh, no. Okay. Remove the okay. But so let's maybe there is actually an error in there. Maybe there is a new error that we've introduced. Alright. But not nothing new has happened. Right. Value fulfilled resolve in collection, direct to this operation has to be unique. Are move the same? Is determine move and return move? Is it the same key? Is that what it is? Oh, possibly, but I really don't think so. This is no. This is called return move. Nope. Nope. It's a return move. You're right. So it works that way around. Cool. Cool. Cool. Cool. Cool. So so then we do this. And it does It's this. This is this is where there's a problem. I feel like our logic is not going to work. Can we just trigger another what if we just trigger another flow instead of doing it past the data? I know it's not as clean but, like, if we're gonna, like, bug solve even just temporarily, why don't we just send it to another flow? It's a new thing we haven't looked at as well because you can trigger another flow. I don't wanna do that. I don't I I want it to work. I mean, we all want these things to work. Okay. We're gonna pause and and we're going to come back when we have worked it out. What was so wait, what was, what was the you're right. The solution was exactly what you described last time. Are you getting error logs in your console that are different than that? Oh, no. Really? Shit. It's a great way to start a bloody director's TV thing, isn't it? Fantastic. Bugs are what make this feel interesting. K. Oh, what? No. No. No. This this one was the good one. Oh, that's yes. That's the reversed way, so it won't work that way. What have I just done? So this goes into return move. No. We want this one to go into determine move. Determine move goes into return move, save error. An error. Delete that card and create a new one and see if it solves the problem. Call return move. Card. Oh, doesn't like that. Yeah. Just return. There we go. Same thing. Director's operation. What is okay. Can we see that that field go into the data model? Can we not see that operations logic? I mean, I mean, yeah, I guess. But, like, we're looking at quite, like, low level, like, built in system. Okay. Cool. There is an error. Copy to action. This is what Riker's just messaged. Hang on. Did it work? No. Okay. I'm telling him what happened. Right. I meant I'm talking to the lead maintainer. Node 13 were. I made node 2 then changed the nodules. And okay. Fuck. Fuck. This is the way to this is definitely what what what was meant to happen this week. Is it okay. What if we run a simple script? Like, just detach that new one that you just created. Add any random script after the determined move. Okay. So we need to create a new one first of all. Right? Yes. Yes. It's great. Do will return data. Great. Right? It's like a pass through. It does nothing. Is it the script? What if we do the same thing with the I Yeah. If we duplicate this script, can you duplicate a flow? Not a whole flow. Oh, yeah. Flow? Yeah. I wonder if duplicating the flow might solve this issue. Duplicating the flow will mean we have to reconfigure the extension. But Well, I mean, let's let's see let's see if it works. Well, yeah. Well, I don't know why I'm doing that. So return. Okay. And we'll copy out. Blows. And then we can Oh, no. I actually can't duplicate 1. You could, once upon a time. No. It's insights. That's insights. Good to know. Fucking Here we are. Now we'll figure this out. We got this. I have I have faith in us. Okay. So our regulars our regular script our no. Our regular script was working. Okay. Skip just to the end. So, like, remove the the head to head and go straight from avoid body to the very end. So skip over the avoid head to head because this is where we started to get these issues. No. It's the final card that's fucking this up. No matter what it is. It's reassign. It worked. Did it work? It worked. Don't question it. Okay. It worked. We did it. What what was our solution, Kevin? We just tried many times and then waited to see what happened. Just wait until I cause many problems to my team. Okay. On our screen. Okay. Alright. Gonna resume recording. Thanks for the click. We got there. We did it. Go team. Go, Kevin. Okay. Hubert, you're welcome. Alright. Ready to come back? We're ready. Let's do it. Okay. And welcome back. We fixed it. We don't know how we fixed it. It just started working again. We will undoubtedly come across this again. Having spoken to like our lead maintainer, he thinks it's a bug, which I will open a GitHub issue on after this recording. Okay. Wicked. So now determining the move and then returning the move. Okay, cool. So we determine the move, which just returns the next move. And then all we want to do here, the literally the only thing we want to do here is return last. Right? Data no. Last. Last? Yeah. Just last. Yeah. I think just last. Date data dot data dot last. Is it? Alright. And the reason for this, if if nobody realizes this already, is because, like, you have to feedback this very specific JSON structure into the response that that Battlesnake precedes. Which kinda sucks. It just seems like, it should. It should just read this the engine should just get it. It should understand at a core level. If you're if you're being told to implement an API specifically, it's fine to have to do it specifically, but we'll test it works. And in theory, we should be able to in theory, we should be able to add another another flow operation here to do logging. Any logging we want at the end of the whole run. So let's manage our battlesnakes. Let's create a game where I'd already set a battlesnake and a hungry bot an 11 by 11 board, and we'll start the game. And that should be running game in progress. I can kind of see my logs over here that the game is still happening. I'll wait for the game to conclude, which it has not yet. You know what? I've run out of patience. Oh, no. It happened. So let's hit. There we go. Excellent. It did end up doing itself in, so there is something wrong, but we're not we're not looking into that bit right now. It worked. That's the important thing. It it worked for some period. Anyway, that's all so it is it is talking successfully, to directors, which is useful. Now what we now wanna do is figure out what we need to log. And before we do this, let's create a collection for it. So we'll go to our data model, we'll create a collection and what are we going to log? I don't think we should log every turn. I mean, we could log every turn. Well, I I mean, we could. I think I don't think it's necessary either. I think let's let's let's right now just log the final move just as like a getting started place. We're just gonna pass that data right through. And then let's do at like, ultimately, we'll end up adding something around, win or loss, because I think there's something cool visualizing that. And then I think there's honestly, I'd be love to explore us actually pulling the game GIF from this, which I think is more logic, but I think it's very interesting should be pretty straightforward. So if we want to log this, if we wanna log the the game, we need to be doing this actually in the end endpoint. Right? We don't want to be doing this in the move endpoint. We want to do it in the endgame. So this actually means we need to a little bit more fiddling, a little bit more fiddling with directors. And to the dismay of our lovely editor, I'm actually gonna unshare and reshare, but I'm gonna do my whole screen this time because No camera job. It's too late. I'm doing it. It's done. It's gone. It's done. Farewell. It's it's gone. Farewell screen. Okay. Apologies for for for that. But what we have here I'm gonna just reconfigure also. See every day, Kevin, by the way? Is this your screen, this large massive thing? It's a 49 inch ultrawide monitor, which will feature in a future episode of What's in My Doc because the final episode of the season is yours truly. We talk about it more then. So if I just make this bigger, we want to we have this move endpoint here. We can delete all of this logic that was left actually. But we have let me just shrink this down a bit. Just a reminder, when we hit the root endpoint, we're gonna hit this flow. When we hit the move endpoint, we're gonna hit this flow, the start and there's end. So what we're gonna do is basically add this logic to to end. So what we're gonna do is, kind of similar to the info end point. You know? The info end point. Yeah. Yep. Yep. Same thing. Yeah. Although that is a get endpoint. We need a post endpoint. So it's more similar to move, I guess. But what we'll do is we'll create a new one. We'll call this snake, end. This can be based on a web hook trigger. It will be a post request. It is a post request. Right? I mean, we'll find out together. I'm pretty sure it's a post request. Yeah. We we will, but I'm also gonna I'm also gonna double check. Look at the documentation. Probably smart idea. Look at the documentation. Not there though. Webhooks. Here we are. Game over. It's a post request as an error hit, by the way. It's it's not formatted as a code block, but it's a post request to slash end. It will be the game object, the final turn number, which is useful, the board describing the initial state of the game board. That doesn't feel correct. Oh, on that final turn, I guess. And you describing your battle snake. And I wonder if there's a death reason, but we can find out. So response body date of last operation. Great. And first thing we're gonna do is just copy this URL. I'll hit save. We'll copy this URL and we will go ahead here and const flow response. I'm not sure we need to, respond to this, but we will Await fetch full URL. We're basically just gonna copy and paste the all of these options because they remain the same. And then we're just gonna send okay. We don't need to do like we actually don't care about the flow response. We're just gonna send all the data by fetch and then send okay. We just need the data. We don't and eventually to respond. So we'll save that. Hear that flow response? We don't need you. We don't need you. Yeah. So inside of our extensions, director snake, we'll run npm run build. Oh, in today's release of Directus, we squash the bug that stopped the autoreloading working. So That's awesome. Nice. Was that because of us? No. Not even remotely. Well, I'd like to think it is. 10.9. 10.9 came out yesterday, so it resolved that. So now we have our new extension. With this new endpoint, we don't need to do anything more there. Back over to our code editor here. I suppose we can just we can actually just leave this be. Yeah. And we can Yeah. Let's just log, see if it shows before. It. Yeah. Exactly. Create rematch. So that whole game is is running. We see that 200 all the way. Really low really low, response. That's great. Running, running, running. And then we now no longer, we remove the console log for end. Oh, this is on start. Have we done it in the wrong place? Everyone, we've done it in the wrong place. I know. Amateur hour. But it is logging it, so we know what we know what the shape would be. Yep. So we can do that instead. We'll make this async. We'll remove async here. We will go and rebuild the extension, and then we'll restart directives. And while that's kinda happening, we can we can take a look at this object here. So we have game, which is an ID, which we'll probably wanna store. The rule set perhaps, probably not for this one, The the final turn, 95. Don't know what we wanna know about the board probably because 95, it's the turn where you're dead. So that's that array will not include us or it will include us and we'll be the last one standing, I guess. You has health. So I wonder oh, maybe we won this one. Maybe we won this one. Let's, let's watch it. Have a sneaking suspicion we did win. Yeah. So wait. How did we how do does it show that for us in that result? Like, it doesn't it doesn't say, like, in our in the law No. No. But I I I wondered why is it still giving me a health of 90? Why did it give me a health of whatever it was, 83? Smart. I have a sneaking suspicion if you die, the payload looks different, but there's only one way to test it, which is I've now restarted Directus. If I refresh this, there should be some logs. So this was the log of the one we just accidentally ran when we shouldn't have. So this is the start. So we'll just forget that one. Let's, let's do a rematch. And now we should only get the response when when it dies. So we can see this. We see it's posting slash snake slash move. And the last thing it should do is post slash snake slash end and it should hit a different trigger. So we'll just watch that happening and this is happening real quick. Wonderful. And that is what happened. It had a different flow at the end slash snake slash end. So if we go back to our flow now and refresh, we should be able to look at the logs. Payload. Okay. So this oh, did we win or or lose? That's probably worth knowing ahead of time. You won again. Come on, ready, set, battle snake. What are you doing winning all the time? You're too good. You're too good. Too good. So 73 turn 73. That is us, ready, set, battlesnake. We know our ID. I wonder if there's something in here where we log what our own Battlesnake is, but it may not be necessary. Let let's keep looking. It has health of 92. It has a length of 6, which could be interesting. We don't need to know what all the food is, where all the hazards are. You is just a convenience again. Alright. Let's, let's literally just keep creating rematches till we die. We'll let this run-in real time which means we we're gonna have to wait a hot minute as every move happens. Are we too Lindsay. Are we too good at programming Battlesnakes that he will live forever? Is that are we gonna need to are we gonna need to, pause? Artificially. I'm gonna have to artificially make him just, like, die. Come on. I think we oh my goodness. Come on. We gotta do it again. Gotta play again. Okay. Okay. Come on. 3rd time's the charm. We did, we did too much, Kevin. We went too hard. That, game in progress thing is not behaving, so we'll restart a game this way. I mean, if I just add ourselves, eventually, we'll die. Oh, there you go. Yeah. And it's interesting too. So, I think part of the game in progress thing might be I know I know Battle Sneak scaled down the server load, so it may be something related to that. Maybe. But, yes, we should live for we're gonna live forever. That's really what you've just done here. You're about to, you're about to kill your local version of Directus just because this will run forever. Hey. Good time. Alright. I immediately got it right and wrong. Thank you. Thank you. I reset Battlesnake. Alright. I mean, eventually, we had to lose. Right? So let's look at the final log when we lose. And I have a hunch. It is that our battlesnake does have a health of 97. So there is a health in there. So that isn't how we determine whether we live or die, unfortunately. We have a board. We have you. Although, where are the oh, the snakes array is empty. So we are not so we are there. Sorry. But we are not present in the snakes array. Oh, okay. That makes sense. So if the snake array is empty, we didn't win. If we are not empty if we are not in it. Yes. If we are not in it. So the easy the easy thing to do here is we have a we know our snake ID. Here it is. We could store it in a collection of our snakes to like factor out or we could just hard code it in. Well, let's let's let's have some fun. Let's let's do the more challenging Sure. More challenging. Snakes. And then, actually, if we look at the if we look at the API, I don't think there is a a meta Battlesnake API is there to get information about games and stuff. No, that's fine. What else should what there could be. Yeah. What if there is there's a start endpoint. I wonder if no, it's fine. We'll we'll we'll hard code this snakes. All we care about snakes is that they have a manually entered string as the ID and we can add all the others later. So if we go to our snakes, we'll put in the ID. That's ours. And you know what? We'll just add a name to it just so we can tell what it is. Right? So, this battle snake is ready, set, battlescape, I actually think is its actual name because we wanted to distinguish the one you made to the one I made. And my name is Elsford k. So it's registered Battlescape. Okay. Cool. So we've got that. So what are we gonna do inside of this end flow? First thing we need to do, I suppose, is we get this whole payload. The only thing we wanna enrich it with right now is are we alive? So Yeah. Is alive. Did live. Did I live? And that's going to run scripts. No, it isn't. First of all, we're gonna get all of our snakes. That's what we should do. Read items from the database. So, get snakes. Permissions from the from trigger. I'm just gonna give it full access so we don't need to contend with permissions. Snakes. If I don't add any IDs, do I get them all? We'll find out. We gotta find out. This is trial and error, Kevin. This is trial and error. Real, real trial and error thing going on here. Alright. Let's create a rematch with ourselves. I feel like I should add another snake back in just to end the games quicker. So do it. Yeah. Do it. Add another snake in. Just go back and make sure I'll let I'll let this game conclude. We'll do it until the next time we do it. Still running. By the way, it looks like games. Oh, thank you. I actually was really trying to find, like, the end of Loki season 2, all of the timelines. I'm not sure if you've seen it. And so it was kind of Yes, I've probably seen it. I've seen it's going for. Yeah. It was kind of that vibe I was going for. Okay. So obviously we eventually die. That's just the nature of playing by yourself. Let's just see how they perform now. Okay. Again, no idea why. Really shouldn't be doing that. But Don't question it. We wanted that to happen. We willed it into being. We willed it to happen. We want you to be chitter. Alright. There there's all of our snakes. So that's great. So that that returns an array of objects which contain IDs which are our snakes. So the next thing we want to do then is did live, like, you know, did live. And we will run a script here. And all we wanna determine here is, what did we call the last one? Snakes. So data dot snakes is the array of snakes and we want to know and we want to know did the trigger dot I need to I don't want I don't wanna close it. Don't wanna close it to to take a look. Let's look at the docs. Webhooks. So it will be trigger dot, I think, payload. I think so. Yeah. Yeah. We can always fix that later. The payload.board.snakes. So we wanna see, did that contain that? So, JavaScript, does array contain other does array contain specific item? I think it's like includes yeah. Does array contain item from other? Right? I'm pretty sure we just did this in our last episode. I think so. Yeah. Some. And I think I used find instead. So, live, which is like the if it lives, it will be found. So we'll say is inside of data dot snakes, is does that contain yes. Does that contain does data.snakes contain? And this is where it gets a bit honky, and I need to just just have a moment to think. So we wanna know out of all of the items in the snake array of which of which we know the I the ID is gonna be snake dot it. We know that already. Does is snake ID equal or does snake ID I got it. Does snake ID feature inside of this array? So what we actually wanna do is return whether this array, the one with all the snakes on the board contains, which I think we've determined is includes includes. Yep. Is it includes? I think we need to simp I think we need to do one more step beforehand which is take this big array here and just simplify it down to IDs. So, const IDs, IDs on board. So we'll take all of that and we'll map it and all we wanna do is turn that into an array of IDs. So it's just IDs. It's nothing else but IDs. Forget all the other information. That's all of the boards. We don't That's all of the IDs of the snake on the board. Exactly. And then we will return whether or not IDs on board that's it. IDs on board includes any of our snakes IDs and we will return to live. So true force, true false, did it. Really? It's gonna work. First time's the charm. Oh, no. I've done it again. I've hit create rematch, But eventually the answer will be false, right, this time with confidence. It should be. Or it'll just live forever and this will be this will be our time. Okay. Hit its hit its own tail. Mounted itself into a corner and did itself in. So let's, look at the last log. Cannot read properties of undefined. Okay. Cool. And it was body, by the way. It was trigger dot body. So that will probably be why. Trigger.body.board.snakes.map. So let's, let's just check that. Body dot board dot snakes. Cool. So we will create rematch. And now second time. And now. Go, red snappettlesnake. So stop eating food. Stop eating food. As it gets towards the food, I'm like, no. Come on. What are you doing? We don't want you to live anymore. What are you why? Why are you doing this? This battle snake is not bad apart from those times it randomly goes off the edge of the board, and we don't know why. I think it's just got a it's got a it's got a spirit of its own. Oh, what's happened there? Oh, there we go. Oh, okay. There was some real They were clearly in a queue or something. Goodness. Wow. We're doing I Oh, there we go. Why did you do that? I don't know. Oh, yeah. Yeah. Yeah. Right. It knocked itself into a into that kind of spot where where it can't. There is no viable move. Right. So we'll look at this. Cannot read properties of undefined. Okay. Okay. What is undefined? What's undefined is what we're running find on. So let's take a look at that. We're running find on data dot snakes. Okay? Except oh, because we call it get snakes. Fair. We'll call that snakes. Great rematch. This is the only thing when you'll see ready. Set up the snake. Sorry. This is the only thing when you're debugging. Go on. And you were saying something valuable as part of this conversation. I was trying to add some color. Wait. You there's there is just a bit of waiting here. Right? I suppose because the rules engine is open source. I imagine people I think people have built, like, line emulators that can obviously execute this way, way, way quicker for the sake of debugging. And I see the value in those tools. Yeah. You can actually do it in the CLI. In the CLI, you can actually play full games and see them. Because we just had to weigh a 153 turns. Alright. Trigger is not defined. Still. Okay. Trigger is and you know what? Because it's data dot trigger. So many amateur amateur hour little mistakes today. Never. Never, Kevin. Amateur is, is a term that we need to take back as a positive As amateurs. As amateur. As amateurs, yeah. Alright. Come on, ready set battle snake. You can die. Don't survive. Please don't. I believe in I I don't believe in you. I feel like he thrives. So the weather so the weather today. Yeah. Oh. Oh. Is that is this it? No. No. It it will lit. It's just obviously saving up moves. Yeah. There we go. Woah. It's like a taunt. That was like a taunt to us. Come on. We're doing exactly as we the thing is we made we made this snake. We made this snake. I feel like we is a very, oh, there we go. Alright. He made himself, really. Alright. Let's see if this worked this time. What do our logs say? Hey. We got data. Yeah. But this isn't what we want. What do you mean? What we want. I don't know, but I just want, I just want, I just want a true false value so something's not right. But wait So, yeah, go on. Well, I'm just like, what are we returning? Sorry. It's the text is really small. Not because of your screen. It's because We are returning, the whole, like, body of the script, which feels a little bit weird. And I have a hunch. I have a hunch. It's oh, no. Did live. This is live. So what's going on? What's going on there? Super weird. That shouldn't be returning that. No. It should not. Let's, look at our previous flow, the move one, and just maybe not, like just take a little bit of yeah. I mean, if we take all of this, and I'm just doing this as a comparison point just so we can kind of dump it in here and take a look kind of side by side, if you will. Yeah. Data dot trigger, Return. We do just return a value. Maybe, let's take a look at, array find method. Let's just understand this a little bit better. This returns the 1st element in the provided array that satisfies the provided testing condition. If you need an index, you can use find index. If you need to find the value, if you need to find if a value exists, you can use includes, which actually I think is what we want because that just returns a true or false. So maybe we just swap this out for an includes, includes, And we wanna know yeah. It's like a nested includes. Fine. And then we return I'm just I'm a little bit confused why it was returning Yeah. The whole code. Well, we no. You know what? We also don't get here. We don't get a returned payload, which makes me think there was some kind of error happening here that wasn't being captured correctly. Is there anything in here? I wouldn't imagine there is, and it's now really hard to tell. Let's, let's create a game. Let's let's make this game a lot, a lot quicker to end. Hey. There we go. Well, do you see how quick that was? That was fast. We are smarter than hungry. Niamis. Come on. What are you doing, Raze at Battlesnake? We want you to not die. We just want you to die. We might we might have to nip out some of the logic or something. I've never had a Well, well, that will that that will be the end. That will be the last move when it finally catches up with that with with Toast. Yeah. Snake end. The game seems to be struggling with the concept of ending, but it has ended. Alright. Yeah. Look. We get the options here, but we don't get the payload. Because if you look at the other flows, for example, wow, 1632. Wow. You see that all of these run scripts. I know they have the payloads. They have the options and they have a payload. Options payload, determine move interestingly interestingly, does does not have a returned payload. Is there something about them returning objects and strings versus just returning like a primitive. Maybe that's what it is. So if I so if I because this could be fine, you know, return. That's returning an object. Right? Mhmm. Let's create a rematch. Okay. Our snake just went up and up again. Hey. We don't know why happened. Bad things happened. We changed we changed the code. Shouldn't have done that. Changed the code. Shouldn't have done oh, there we are. Did live force. Okay. Awesome. Cool. So so there was actually no problem. Just an interesting note there that for it to show up in the payload, it needs to be an object or an array, I presume. So just a good just a good note there. I would love to play a game where we don't lose. So we can just so we can just check both sides of that equation and that they make sense. Let's create small boards. If we play with ourselves It wouldn't then technically, we'll always win and always win. Oh, well, yeah. But then I think it's just gonna get a little bit weird. There we are. Hey. We no. Wait. That's not what we want. That's what we want. But We've wanted to lose so much that now to win seems like a wrong thing. So did live should be forced. Yeah. Live should be forced. Let's create a rematch. Maybe we need to put in a a a a less clever snake. Goodness. Okay. Try Do you want the right bot? Yeah. Yeah? We we haven't we haven't gotta live long to make that work. Okay. Great. So now if we refresh, we look at the logs. Did live hopefully should be False. Absolutely. That's totally what we were wanting to have happen. Great. Is this really what we're gonna spend this episode doing? Okay. Fine. That's it. Just debugging JavaScript. That's really what we're here for. Right. All we want to do is now maybe maybe we just roll back for a sec. We're doing all of this grab snakes thing. I don't think this is adding I've deleted it. I've just done it. I've just done it. I've been busy for decision. Exec executive decision. All we care about is this ID. Right? So let's just we'll we'll delete this. We're gonna hard code it in now. Okay. Yeah. Yeah. Like, I don't think it's worth, like, going at all costs. Right? So all we want to know is does data dot snake say const? In fact, I think we can say, does snake include data dot trigger dot body dot board dot snakes dot map. Sure. So that's just a set of IDs. And all we wanna know is, does IDs onboard include this? And I'm I'm assuming can I console log inside of these? Best bit of, debugging we got. Great rematch. Well, I think our snake's broken. I think we spoke too soon about the capability of our snake. In it doesn't like includes. Payload.body. Right. Hang on. It doesn't like includes, which is this one here. And we don't get a log, annoyingly. So all we care about, Let's, you know what? I'm actually gonna be bold here and just remove this, and we can start we can start again. So so all we wanna know is did it live? And to know if it lives, we're gonna go through oh, no. I do need to I need this too. I like it. I like the I like that you were confident in the removal, but also truly confident. That I you know, I really wasn't. So all you wanna know is, does data dot trigger dot body dot board dot snakes, does that include includes includes let's take a look. We need to give it a specific value here. So I think what we'll first do is we'll say snake IDs on board. We'll take this and we'll map it once again. So we just extract the IDs. Then all we wanna do is check whether snake IDs on board includes this value here. It's all we wanna know. Please work. Create rematch. We've got this. Okay. Our snake just keeps going up by the way. We fundamentally broken something. But I mean, these things happen. These things happen. We'll figure it out. We've got Live force. The rest of today. Live force. Okay. And we did we did we did live force. Yes. That is that is true. And all we need to do is outlive the snake, which we did, and hopefully live is true in this context. We may need to go back as as we commented in Okay. But we lived, but we lived. Let's let's take a look at the payload. Let's take a look at the payload. So body dot game, no. Body dot board dot snakes has ready set battlesnake present, and that's it. The snakes have an ID. That's our ID. That is I'm just gonna double check. Yep. The ID, nope. Not the ID we've been pasting in. What does it get a different does it well, it's different. So I'm wondering, look at this ID, w d at the end. If I look one game back, it gets a different ID every time, Andrew. So we gotta pull name then, I guess. I guess the ID is not I'm literally so mad. Yes. Fine. As well, you should be. Damn you, battle stake and your your ID that is not truly the snake ID. Who do you It does feel that way. I can't believe we sunk so much time into that. Okay. Cool. No. Right. Create custom game. Create rematch. Okay. We lost because apparently we just go up now. Yeah. Liveforce. Cool. And create rematch. Yeah. I mean, we lost again. Sure. Because that's what's happening now. What is going on with that, by the way? What is going on with that? Is it is it work. Are we struggling under the number of logs that are being generated? No. I mean, possible. Look, avoid head to head force. So there's something in the avoid head to head. Oh, god. Because there was no head to head issue here. No. There wasn't. Let's create rematch just until we get a a seed. I think, yeah, we both die at the same time there. Let's create rematch. Let's just create a seed just to test out this logic. Yeah. That's good. Nope. Not good. Okay. Create game. Okay. We we broke it. We broke it. Okay. We are still not gonna do well this oh, what is going on? There we go. Is it there someone is there something is there something about we did? Is there something about the board size being different that's throwing it off? In which case, I'm quite happy to say how Battlesnake only operates in 11. That's the right answer. Don't make it work on all the boards. Only standard class. True. Okay. Hey. There we go. Finally know whether it lives. Now it's all about logging information. So let's look at this payload and see what we have. So we have a game ID, which I think feels like a no brainer. Right? Yeah. And you said we can do something with the ID to get the GIF? I believe so. I'm gonna go check and see something right now. I'm gonna go over while you're doing this, I'm gonna go over to my other screen. But, yes, we'll pull back so that we need that. We know the turn of the final move, which is 2. Yep. Right. So that's the term where we were victorious. We know that we lived. We know our length, I suppose. Yeah. I think so. Yeah. And maybe we do maybe we start with just games. Games get an ID, which can be a manually entered string because we'll we'll pop that in from the API. And I think all we need here is win or lose, right? Win. That's a Boolean. We wanna know what about it. Like, a turn, I suppose. That can be an integer. I mean, game mode? Turns. Yep. Turns. Turns feels better. Yep. Turns. Into game mode, but we're not playing around with other game modes. So I just think That's true. Yeah. It's fair. Yeah. But but the idea is, in theory, you could you could store all of this. Wind turns. What else have we got at our disposal? Let's take another look at our end state here. We have live die. We had turns. We can start with how many snakes there were on the board from the trigger. So snakes dot so trigger trigger body board snakes length. Just quickly gonna go, store. We're gonna prepare data. So trigger dot body dot board, did we say it was? Dot snakes.length. So I'm just gonna pop that in there for later. Yeah. So snakes, that can be another integer. What what else what else do we wanna know? I mean, let's let's let's stick it with that. Good because we you could do, like, a win ratio. You could do, like, average turns per game. Like there's lots you can do here. Do you continue getting data if you are not the last to loot? Like if you die and it goes on a few more steps, do you only get the end state on your final turn or do you like, let's take a look. Your battle snake will whenever a game it has playing has been ended, use it to learn how you battle snake won or loss and deallocate any server side resources. Okay. This was like a good starting point. So this is what a game looks like. ID win turns snakes. So going back to flows, let's, do our end. So did live. We're gonna prepare data. So the only extra thing we wanted to know here was snakes. Snake count. What else do we wanna know here? We we might as well get it already to be fair. Might as well just do it straight within here. So snake count. Is this. We have we had snake count. We had turns. So once again, let's just take a look at the it was game. I think it's game dot turn. Game? Maybe not. Bored? Bored? Bored? Hold on. No. Am I using it? Oh, it's right here. At the root, turn, integer. So that should just be trigger.body.turn. And we wanna know we wanna tell how many things. We wanna tell how many turns. We wanna tell whether we lived or died that we needed the ID. ID. So trigger dot body dot the ID. I think it's just I game dot ID, I think. We'll find That sounds right. Game dot ID. Yeah. Nice. And while we're here, let's, let's just also get the did live. So live. Live? What did what did we call it in our director's collection? Lip libs? Let's take a look. Was just libs? It's called win, actually. Win. Alright. Smart. Win. So, oh, so in here, we'll go prepare data. We'll go live, but it's called win here. Live, live. Fine. Whatever. So we have, I really don't like using last or I can help it because it allows you to obviously move things around without your data becoming like brittle. So I'm gonna pop did live. And all of these have to be prepended with data. Okay. So in theory, that's the preparation of the data. Let's, let's run a custom game one more time. If our snake performs well, it will have been the 7 by 7 board that did it in. Well, I think I think oh, of course, we've just got let's create the game with the ready set battle snake and the hungry bot now. Yeah. Weird, isn't it? Anyway That's such a good idea. I wonder what we did. Don't don't wanna know. No. That's a question for, for season 2. That was a question. Set back. That was a question for last week of and now it's this week of do you see how big that snake's getting? Oh, of course, it's hungry, but isn't it? That's its whole that's its whole shtick. Are we gonna win this one? Oh, we won it still. Yes. Legit did. So Well if we refresh here and look at our log payload, snake count 1. This is wrong. So we need to just look at this because we wanna know how many snakes were in the whole game, which in theory should always be no. It won't always be this. End. We're pulling from end, so it's only gonna pull the last snake. Right? Is that what we that's what the Yeah. Maybe we maybe we need to do it on start. We need to create it. And then on end, we need to update it. We have turns, we have the ID and we have whether we want, which is true. So maybe for snake count, because I do think snake count is interesting. We should create just create the data. What do you think? Or do you think it's too heavy handed to create a whole flow for that? I mean, I don't think we need a whole flow for it. That seems like a bit of a change. I mean, we need we need a flow for it. That's the truth. So may I mean, we we've we've got pretty we've got pretty proficient at it now. So what we're gonna do is create a flow. Yeah. Yeah. We'll call it snake start, Right? And so what we're gonna do here is we're gonna do this as a webhook. It'll be a post request. We'll go like this. Just double check the API reference webhooks. Your request to this your response to this request will be ignored. We don't need to care about our response. We'll save this, grab this URL, and then we are going to take this whole end here. And on the start, we'll make that async. We'll paste that in there, except this time we will change the URL to this 1, 704. So that start, we need to just kill this, go here, rebuild our extension, Done. Go here and Docker Compose up. Wonderful. Should be able to refresh. Should be able to refresh. Now I think it just took a moment just a moment to spin up there. And what are we gonna do here? We're first gonna save. We're gonna go and create a game. This will need we don't even need to wait for the game to pan out in order to look at this one log. All we care about is I suppose number of snakes here. So trigger, body, board snakes. Trigger body board snakes. So what we'll do here is create sorry? Board trigger body snakes. No. Trigger. Shouldn't be Stop it. Shouldn't be Stop it. I shouldn't do that. I'm sorry. Create game. We will create data in the game's collection with full access. And all we're gonna do here is we're gonna say the snakes is equal to. Trigger body board snakes. I got it. I got it. They need to be in quotes because it's Jason. Trigger Jason or Jason? I I am a JSON. Jason. There is one there is actually, extract snake count. We just need to get the value out in the previous step. I don't strictly think you need to do this, but just to keep it really clean, what we're gonna do here is we're gonna return snake count. Snakes is equal to data dot trigger dot body dot board dot snakes dot length. Snakes. Copy that key. And then the next one, we will create data. The name and key doesn't really matter because it's our last step here. All we care about here is we need to create an ID at the time. Oh, but we can create the ID as well. We can get the ID as well at the time of creation. So snakes is going to be equal to, this snake count dot snakes. And I actually think we can ignore this error. There seems to want a string, and I think that's fine even if it's, an int just to stop it moaning. And then we also want the ID, which is going to be oh, oh, throwing my cursor around. Here we are. Trigger dot body dot game dot id. So if I save this, we create a new game. Oh, got a little Game in progress. I don't think it is. Our snake 1, thank you very much, but we will create a new game. That's fine. Start game. We immediately should get a new log here and we got a trigger but nothing else happened. Why? Oh, no. There we are. Create data. Type error cannot read properties of undefined snakes 2 body trigger body game ID. Okay. Cannot read properties of undefined reading starts with. Interesting. This looks Looks good. Looks like what we want. Snakes and ID. Is that what we called them? Snakes and ID. What is going on? So star. Shrek snake count. Because in theory, this is looking oh, pick collection. That'll do it for the access. Not telling it to do anything. Alright. Create game. I've got really you get really proficient at starting new games with Battlesnake, don't you, when debugging? You do. Alright. Did it work? Oh, there we go. Now how That's the idea of a new item, item, which means we now have a new snake. Wonderful. So now going back to our end state, we don't actually want to so let's refresh ourselves. Prepare data, prepares a snake count, turns, ID and win. We do not want a snake count. We do not want a snake count. So when we prepare data, we can remove snake count and that leaves us with turns, ID, and win. Yep. Turns, ID, and win. Turns ID and win. And, actually, we don't even want the ID here. No. We we do want the ID because we wanna be able to pull that GIF eventually. But we stored it. It already exists. Oh, that's right. Yes. You're right. You're absolutely right. This is what we're gonna This is what we're gonna so all we're gonna do here is now update the data Lovely. In the game's collection with full access with this ID, trigger body game ID, and the payload, I think we might just be able to put in lost and I think that's gonna be equal. I'm not a 100% sure, but I think that's gonna be equal to the object. So we'll see. Alright. The first thing we'll do is create a new game, and then I think we'll be ready to jump into insights and see how that works for us. So here's our snake performing well in these apparently quite controlled conditions of an 11 by 11 board with 1 other snake. Okay. Come on. Some something happened now. Come on, hungry. You don't wanna survive. You're not really that hungry anymore. It could be all all one either. You both suck. Hey. Hey. So first thing to do is let's see. So you will notice nothing happened there. I've also, to be fair, noticed we don't have any we just have IDs. We probably do also wanna capture, like, the date, like like the now date that's happening because, we just wanna be able to sort the latest item if possible. Yeah. That's fair. So we'll create a date time, finished. We can call this. I think this will work. Date time. Sure. And then at the well, firstly, that didn't work. So we don't wanna spend too too much time on this, but we'll prepare the data here and we'll say what was the name of the field? Do you remember? Oh, it was, we didn't call it datetime. We set it today. No. Now? When? I'm not sure. Jeez. That that's terrible. Both have forgotten Finished. The minutes of creating it. I mean, same. Same to be fair. Finished. We also wanna store finished, which I think you can just say new date, new date dot now. Is that how you do it in JavaScript? Your guess is as good as mine, sir. Yeah. New date dot now. Cool. But it didn't work. So as much as this is cool, what happened here? We updated it. We updated it with the key. Oh, do you wanna know what didn't happen? It isn't showing here that we had a payload. So I think there was an issue with the payload. Oh, look, the payload didn't save. Interesting. So I'm just gonna double check that that is what happened there. If I hit save and then step back in, maybe it shouldn't let me do that, but this is okay. We're not gonna lose much sleep over it. Finished is going to be, no, last dot finished. Then we had, win is going to be last dot win. We'll wrap that in quotes too. What was the third one? What what are the 2 that we have now? Win, turn Yeah. Turns. I believe. Like a living rubber duck. Last stop turns. Turns. I'll hit save before we, you know, spend time on a full snake. We'll take a look. Turns and win. Turns and win. And now we have finished. Turns, win, finished. Turns, win, finished. Great. So let's save all of that. Let's create a rematch. We should see, hopefully, that this is stored. And then I don't think we're gonna spend much more time capturing data. Instead, we'll run a few games, and then we'll try some insights. Nothing's happening still. So still something done in our logic. Let's look at our latest I'll just refresh it. Let's look at our latest log. Oh, intermediate value dot now is not a function. I actually know why this is the case, and it is because, these run script operations running what are known as JavaScript isolates, which are these incredibly limited JavaScript like runtimes that only contain pure JavaScript objects. Let's actually take a quick look at that because I think it's interesting. Isolut. Yeah. This page looks legit. That's not what I want. I do kinda wanna show you like pure Java script or, objects. This is what I'm looking for. What I'm looking for is just like, where is the list? Am I am I what's happening? You might be imagining. You might be imagining this. You may have already used JavaScript. I just I have imagined it. Created it. Maybe you created. There's no documentation. What did you call them again? Isolates. Isolates. Interesting. I've never heard of that. Isolates. And now is that just a security thing within Directus to ensure that, like, bad actors So this So this actually happened in version 10.6. We had I think this is quite what I want. We had prior to version 10.6, you could build, you could use NPM modules inside of extensions. Extensions. Well, video has gone a bit funny. I'm not sure if the is fine now. It resolved itself. Anyway, and the library which we use to isolate flows from each other and operations from each other basically became insecure. Like, it was it was became known it was insecure, and so we had to remove it. And in doing that, we replaced it. And now it now it runs what are known as isolates. And this is what I'm looking for, I think. 1st class. Nope. I'm looking for oh god. It's really bothering me, but I will take a moment to find it. What's is it in our docs? In breaking changes 10.6, we dropped support for custom npm modules in the run script operation. Prior to this release, directors relied on VM 2 to run codes in the run script operations and flows. VM 2 is now unmaintained with a critical security issue that could potentially allow for code to escape the sandbox and access the machine, which hosts directors. So we've migrated to isolated VM to allow flows to run safely. This basically means you can't use just arbitrary NPM packages in flows anymore. You can still turn them into custom operations, but it isn't isn't here anyway. I learned a lot in during the process of this. There are lots of things you use in JavaScript, which you think are part of JavaScript, things like set interval, things like console log, set time out, fetch, that you just think, oh, this is part of JavaScript, but it's not part of JavaScript. It's either part of the browser's implementation of JavaScript or Node JS' implementation of JavaScript, many of which are shared. Isolates don't have this. Isolates just have pure JavaScript functionality. And the I I what I was trying to find was the list of what that meant. I've I can't find the word off the top of my standard objects. Boom. Found it. Thank you. So this chapter documents all of JavaScript standard built in objects. So you have access to global this infinity, not a number undefined. You have these function properties only. You have some fundamental objects like object function, Boolean symbol, a few error objects, a few number and dates, a couple of text processing functions, a few on arrays, key collection structured data and so on. What you'll notice here is a lack of console logs. What you'll notice here is a lack of date, which is what I was which is what I was going for there. We're not gonna lose any sleep over this. I'm literally just gonna we'll remove finished. It just isn't worth the time right now. You know, I do like it though. I wonder if can I add can I add after the fact a date created? Or did that have to be done at the time of Based on my I think it might have had to be done at Yeah. I just scroll down. Though. Exactly. And then just flip it over. I'm just taking a look. There's a sort field, which is great. That's okay. Take a little take a little cheeky screenshot of this so I can refer back to it. There we go. I'll put that off at the site, and then we'll delete this collection. I there might be a way to do it, but I I don't have the the patience. If I see But what I want is date created because I think that would be interesting. That's all. I just wanted that one extra field. So half width. So we also had in here a win, which was a boolean. We had turns, which was an int. We had snakes, which was account. It was a number. It was also an int. And we had finished, which we've effectively replaced with a date created. So half, half, and whatever. Half. Okay. So but but so they run-in isolates. And isolates what it taught me was all these creature comforts I have in JavaScript are not in JavaScript, they're in Node. Js or they're in the browser implementation of JavaScript. But, yeah, that's how I basically learned that. There you go. I had no idea. I had no idea console log was not built into JavaScript. That seems so odd. But I guess the console you're dealing with is in the browser, so it does make sense. Yeah. Let's create a new game. Let's create a rematch, see if we can get it to work. But, yeah, just fascinating. Yeah. Isolates. And isolates are not, you know, while that's running, isolates Cloudflare. I wanna say Cloudflare Workers. Yeah. Here. They have some really good reference while I was learning more about it. Isolates. VA orchestrates isolates, lightweight context, provide your code with variables that can access in a safe environment to be executed within. You could even consider an isolate a sandbox for your function to run-in. A single runtime can run hundreds of thousands of isolates switching between them. And isolates memory is completely isolated, so each piece of code is protected from other untrusted and user written code in the runtime. Islets are also designed to start very quickly. Instead of creating a virtual machine for each function, ISIT is created within an existing environment, blah, blah, blah, blah, blah. So but what's interesting is they are they are way more limited. Yep. Yeah. I wondered I wondered why because I I think what did I I was reading yeah. I've been reading through the documentation, and it said that the the specific JavaScript that you were able to call within within lots of these areas, the directives were limited. And I was like, oh, that's that's an interesting and I figured it was security, but I didn't realize there was so much to learn here. But that is why you come here to Ready, Set, Battlesnake is to learn things about Battlesnake and JavaScript and directives. And it worked. And it worked. Let's go. And it worked. We did it. So what we're gonna do now really is we're gonna just hit create rematch. And if I can I create loads of games in in, like like Yeah? Yeah. Yeah. It's gonna break things. We we we have also not built just a moment. We have not built logic to know which game is happening. We'll do one at a time. Top of them, we have. We have. We have. Because every turn is individual on the move endpoint. And then on the start and end, we're referring to the IDs. Actually, we could be running a load of these in parallel. Alright. So give it a try. Let's see what happens. See what happens. What's the worst that could happen? We end up having to pause our episode because we made a mistake and we have to fix it off camera. That is the really the worst that could happen. Oh, I am a bit concerned now because I don't even know when they've completed. That's another problem is I actually don't know when they I've taken a a guess that they won't take longer than each other. Like, alright, that game's over. I mean, let's just see. There you go. We're not winning every time. We have a number of turns. We have a number of snakes. Let's, let's just create a few more games. Like, I I want a a reasonable set to start with. All of them are gonna start with 2 snakes. Go on. I was gonna say just so if you, like, if you're if you're watching this and you're like, oh, I don't wanna have to, like, do this. It's it's causing my browser. I don't like all those tabs. You can, like, do all this from the CLI too. Like, you can actually go in and just, like, continually create games for the CLI. This is just easier than going in and setting up the CLI right now. And I don't wanna set up the CLI in this moment. So we should actually know if any games are outstanding because, yes, see, there's no turns and no wins on this one, which tells us that game is pending and now it has concluded. So, actually, we do have a little indication of how games are going. Look at us. So we'll create a few more games with a mixed player count. Okay. And if we refresh Directus, we should see okay, there are a few games still pending completion. If I refresh, 2 games pending completion, refresh 1 game pending completion, refresh. That game is still going. That game is still going. That game is still going. Oh, still going. We're doing well. Is that is that the last one we created? I mean, we'll find out. It must be. I hope it is. Descending. Oh, they've all concluded now. Okay. I think that's good to start. So Yeah. Let's go to insights, see see what we can do. Snake battle station. Let's pick a little kiss. Pick wonderful. Pick a little thing here. Is there a I don't is there like a snake? No? Is there is I don't want this game. Yeah. Cool. And our battlesnake. You know, do you wanna know why green feels like a very snaky color to me? It does. Save. That is the right one. Snake battle station. So this is, director's insights. It's this little module over here. And here, we can create dashboards. I don't really use it very much personally, just because my use cases don't demand many dashboards. So there might be a little bit of learning here. You can create panels. Just as a note, this is panels also an extension type. So if you wanna, you know, build on top of these panels, you know, build additional panels, you're more than welcome to. For this, I think we're going to stick in here. You know, we have the pie or donut chart, which feels like a good starting point. So we're gonna look at exactly what I had in mind. So win, Win. Count. Sure. Don't care about donut. That's just a display thing. Show labels. Yes, please. Show legend. Yeah. Pro oh, do you wanna know what? Let let's just see what happens here. Can because I think we can do conditional styles as well. So win rate. Okay. Hey. Dang. That was I don't wanna say that was too easy, but that was very smooth. Yeah. Although I we do need a legend. We do need a legend quite desperately. I would just always assume the the bigger one is my win rate. That's really It it you you are correct. Alright. Win rate win no. Win rate feels good. We can do, games, you know, we could do, the x axis is this one. Right? So date created maybe and turns, like how well am I doing over time. Yeah. Yeah. I like it. Aggregation. I don't really know what I'm doing here. Well, okay. Just leave it leave it without aggregation and see what happens. Yeah. Sure. And we'll call this one what are we doing here? Turns. Turns feels like a weird one. Maybe we just do turns feels weird. Why don't we do, like, account? Because I think what it will do, that's tell us the number of games. Sure. Let's find out. Right? Right. Games over time. Of course, we'll be looking at a very micro Woah. I'm not sure this is gonna pan out. This totally is not correct. Okay. Something. We did a bad thing. Aggregation can be forced as well. Right? Can you? Well, set it as false to see what happens. See what that change does. Oh, didn't like that. We are not doing that. I guess we do need to have Oh, aggregation is gone. Okay. So we have turns. We have snakes, we have x axis, which is date created. Do we need something on both axis? I'm sure. Do we? Oh, maybe not. I mean, it kind of looks like we do. I mean, I'm kind of as you can tell, I really don't make a lot of dashboards because, you know, it's just not it's just not my world. Maybe if we let's let's let's, see what else we can do. We have I like metric firstly, which is like total games played. That one feels easy. Yeah. That's simple. Aggregate function, just count. Count. Yep. And, suffix, Games played. Star decimal unit. Notations standard. Yeah. Yeah. Oh. Uh-oh. GraphQL validation error? I think I'm just not I'm genuinely thinking I'm just not selecting enough field. Do we need to do a field ID? Yeah. We did. Oh, there we go. 15, 15 groups load. Nice. Sure. Okay. It's not pleasing my eyes very much that, but let's, keep taking a look. Yeah. What do we have still? Font size auto, which makes sense, or you can make it fixed and then you can add padding accordingly. I do get the auto. Auto makes a ton of sense. We have a line chart. We have time series. So the collection, once again, games, the group aggregation, oh, group precision minute because we set a few games off at the same time. Count, date field, date created, date range, past 15 minutes. Sure. Value field. I, turns. Snakes. Oh, I don't know. I mean, can I edit can I can I oh, there it was? Can I clear the value, please? I'm just gonna hit tick. Alright. Didn't do anything. I've picked I've picked the wrong combination of picked the wrong combination of options. We almost we almost did it. Did you see how confident I went in? You went in. Listen, confidence is half the game here. We just gotta go in thinking you know what you're doing and, eventually, you know I'm not really sure. I'm not really showing this bit of director stuff pretty well. No. I think it's I think you're doing this fantastic. I like that we've been able to get stuff like we so it's because I said it was so smooth. Hey. There we go. Nice. And these are showing us? So this is showing us the number of turns taken on a minutely basis. And that was where I set loads of games off, and this was just as it was coming down from the last data point that was held. If I do it the past 30 minutes, I think I'll get a slightly oh, maybe not. It's a good try. Yeah. This is oh, here we are. Automatic based on data. That feels good. Okay. So 80 turns over here and then 320 turns over here. And then if we create a couple more games now, we'll create a game. Yeah. And I do like this. Like, if you were actually looking at this over a period of time, this would make a lot more sense. Could be really insightful. Yeah. So let me just save that, and we'll just make sure. Oh, thank you. Woah. Last game has not concluded yet, so we'll just let that conclude. Okay. So if we go back to insights now, there you go. It's a little bit more of a of a view. Right? So I don't really wanna look at the number of turns though. I wanna look at is it count? I think that will give us the same graph. Yeah. What I want is I just wanna know how many there are. How many games? How many how many games? Do you think turns is interesting? I no. I mean, I think let's I so turns is interesting, but only if you're playing 1 at a time. Exactly. And I think I think I think there's a way to do that. I've seen other people do it. So we had I think it might this global variable variable key can be like game. Confidence. Lots of confidence. Game interface. That's not what I want. Type. Is it in here? No. I've seen it. I've seen it somewhere else. Is it maybe called global relational and you pick a collection, you pick games, display template? Yes. You find the ID. I think this is it. So you select a game. So it says select an item. So can I, whatever? But we we won't look at editing that. So you select a game, right, like this. Great. And then you can use that global variable in all of the other boards Okay. In all of the other panels. So if I you know, maybe we do this. This is like global values up here. Great. And then we have this, you know, dynamic portion. So what do we want to know here? Well, we want to know I I don't know. Like, what about are we doing we're not doing per turns. We're not storing turns too. Maybe we just do what do you think? There's only one other thing that I thought would be really fun, but I think that the logic is gonna take too long. No. And it I mean, not gonna be related to this panel. I think it's I I'd be very interested to pull in some custom that that the the actual gift for the game. I think there's something interesting there to be able to pull in. I think we could do that. I think we could do that. Yeah. That's an ambitious it's an ambitious goal. You wanna try? Yeah. Go on then. Right. Tell me how to get the tell me how to get the URL. Go to your game. I'm I'm going. Got it. Alright. Go to view as GIF, and then it'll give you the format that you I will only click it when you say it correctly. GIF GIF. You may. GIF. Okay. And there's a URL and that's the game ID. Grab that, And that's that ID is movable. Right? Like, that ID is the ID of the game. So you can buy back and game by game. Yeah. So let's try our hand. We need to do a custom panel. That is that is definitely what we need to do. Yeah. But, right. So what are we gonna do here? We're gonna go up one layer. We're gonna go MPX create directus extension at latest. Whoops. Extension. Helps when you type it correctly. Right. What we're gonna do, we are oh, you're you're putting me on the spot here. I'm not overly confident. And viewers, if we don't get it done, we're not gonna get it done. But let's call let's consider this a stretch goal. I also haven't used these global variable values too, too much. While that's kind of doing its thing whoops. Docs, Data Studio app. I think it might be over in the user guide, insights, panels, global relational value here. Oh, and then in other in other, ones, you can use the variable key like in other panels. That's interesting. Anyway, we want a panel, director's extension, snake panel. JavaScript install dependencies. Okeydokey. I'm glad I gave you this challenge, and I am not the one having to implement it. This seems right. This seems good. It all seems good with the world. Alright. So we have the panel here. We have our panel dot view. It has been a while since I built an extension. So here we have all of the configs. We'll call this snake GIF snake GIF. Sure. We could do loads of configuration. We're not gonna do that. First thing we're gonna test is if we just have a if we just have a string like we manually insert it to start, can we can we play? Can we play it back? So in the panel, we have this text here, and I think all we would need to do, there's a bit of an assumption in here, is we're gonna paste in this URL and we're going to interpolate here the text, right? That feels right? It feels very good. Npm run dev. Sorry. Npm. I'm I'm not even in the Npm run build. I wasn't right running the right command or in the right location. And then we will I think what I'll do is I'll can I hang on? Kinda wanna open them side by side. There we go. And then I wanna just kill that, restart it. Once again, fixed as of, like, yesterday's release. I just haven't got around to upgrading this one yet. Open up our panel, and I'm hoping snake GIF. Hey. We insert an ID here. Let's go get an ID. This one, 72 turns. And so I I can't believe it might actually why did I say that? You've jinxed it now, but it's okay. Here. Hit save. Okay. Save. And oh. Don't know, mate. Happening. Oh, I have my dev tools kind of detached. Content security policy. So this might actually be cross it might be course with the dev side platform. Which is which is, the ilk of what this is. How do I solve it? How do I solve it? It's a GIF. It's a GIF at a location. Can we import it maybe? Can we import it? And then how would we import it? Let's take care of let's let's consult the docs. So we have in here extensions, developing extensions, extension services, accessing files, import a file. This is inside of an API extension how we would import a file. We would instantiate a file service. We have an asset key, which has the URL and a file object, which contains additional metadata, and then we read it in order to to return it. Excuse me. So let's have a think about the right way to play this. We could press a button. It'll import it and then play it back. It's a good idea. Let's try that. That seems easy. Let's try that. I say this. I say this. Easy as a That sounds easy. That sounds easy. Kevin problem. Sounds like an Android watch, and Kevin solves this tactical challenge. Alright. Alright. Alright. So let's just keep this easy for a moment. Let's let's stitch this off, and let's create a new bundle. And I'll call this one directus extension, and I'll just call it, Battlesnake Viewer and install dependencies. So that's creating a bundle. In that bundle, we'll have an endpoint and we will have, and we'll have an endpoint and a and a panel. So we'll CD into the battlesnake viewer, and we'll go npm run add. And the first thing we'll add is actually the endpoint. We can just call this one importer. JavaScript. And now inside of Battlesnake Viewer, source importer index dot j. So this is again, our custom route as mentioned earlier. Now inside of our existing one, we've actually got this is snake GIF. We have our actual snake here. We're gonna we're gonna just kind of nab this structure for a moment, inside of the importer here. We'll remove these actual things here, like so. Okay. So we'll call this one importer. Right. So when we hit importer, what we're gonna do, and we're gonna do a post request here, is we are going to access files. I'm gonna copy all of this. Here. We'll see what we can do. Oops. Okay. Router dot post. We will create a files service. Now I actually think, the docs do talk about importing that. So let's just take a little look at the top here. Yes. Okay. So in the endpoint itself, we have a router and a context. Awesome. Let's do a let's do a little more side by side jobby here. Right. So we have router and we have context. And up here, we're going to grab these values out. Then inside of router.post/cool, we will create a new file service. We will look at rec. So file.url is going to be req.body. URL. Cool. And the file object itself, how much of this must we pass? Let's look at the file object. API reference files. File object looks like this. How much of this must we pass? I don't think any. I don't think we need any of this. So I think we'll remove that. So we have our URL. Then we will import it. It will come back. We will then have the asset key, and we'll return the data. So if all goes well, npm run build, This is our importer, docker compose up. And I think what we wanna do here is test whether this endpoint works. Now I still have ngrok running. I don't still have it running. I've been running it since the beginning of this, this session. And I have hopscotch here, which is similar to Postman. It's just like an HTTP Explorer. So here, if I go importer post and inside of the let's just hit send there. Okay. Timing out, timing out, timing out. There is an error, invalid URL, input object object. Okay. Let's just cancel that. Let's set a body. Let's set this to be application JSON. There we are. And what we want in here is a URL of and then this exporter battle sync. So hopefully this will return the ID of a new item in our directors project. Okay? It's still going. So not feeling good about that. Invalid URL. Is it, though? Is it? Apparent I mean, no. No. No. Object object. Import 1. So it was struggling with import 1. So let's let's take another look at this. So it was struggling here with the file service dot import 1. Req dot body dot URL. Now to be fair, we never once took a moment to check whether it was gonna be req dot body. So let's just res.sendokay for a moment and that's console.log req.body. There was a bit of an assumption in there. So we will rebuild that and then we will restart that. Okay. And we will send that and we should hopefully get an okay back. We did. So let's take a look. So inside of req.body, yeah, dot URL. What's going on? Is this a URL we can hit? Oops. Yeah. Should be. Yeah. Sure. And let's just test that there's nothing funny going on with, like, access here. If I go to our file library and import, this is the RF for importing and hit import. The GIF is there. So there is nothing wrong with that. So we're just doing that via API instead. So fair enough. Fair enough. So we have the URL. We have some accountability, which I think we might just be able to bundle some for now. Get rid of it for now. Let's do npm run build and restart the container. Go to hopscotch and send this. Okay. Great. And this time, we've got a URL, which means there was nothing wrong. So let's just do one step at a time. That's console dot log the asset key. Yep. Rebuild. I'm determined to get this to work now. We are gonna do it. We have we have the Cool. Technology. We have the ability. That should still just return an okay, but it hasn't. Why? Code error invalid URL. It gives me it gives me it gives me the docs might not be right, which is possible. So as much as I wanna just be like, but that's what it says to do. Let's take a moment and let's actually, if we go to accessing if if let's find let's find the docs for this and let's just step through because all of the services are just in the code base. It's not my preferred way, of course, to be looking at stuff. Let's take let's take a little look. What we care about here is import 1, and it does expect an import URL and a body partial. So maybe maybe we did need to pass in, could not fetch from URL. The error said invalid URL out of interest. So maybe yeah. Because what it does is it grabs the raw file and then uploads it. Maybe we need to we must pass in this body. Just a thought. And that was the partial body objects. And maybe I mean, that's a partial body object. Maybe there's something else we must pass in this at the time of creation. Let's, let's hit send. Come on. Okay. Okay. Okay. In fact, again, it's taken that long. Yeah. No. Input object object code invalid URL. That invalid URL. Couldn't fetch file from URL object object. Isn't that interesting? Because the URL to me is a is a string. Right? Yep. And found that URL. But this time, before all of this, I logged service external file is unavailable. Couldn't fetch file from URL object object, but it isn't. It is a string, and this is the string. Fascinating. I mean, let let's just, you know, I've been a bit cheeky here. Maybe we, go back to accessing files, and I do actually create it with this accountability object here. Yep. I'll take that. I'm pretty sure I know it's not needed. I know it's not needed. But that the error isn't saying something else is missing. You've triggered an unhandled rejection. You may okay. Yeah. Fine. Okay. True. So if we go if we try around this, right, and then we catch an error. We console dot log the error. And we send this. And We are expecting an error, which is fine. We've got 503 service unavailable. Why? Couldn't fetch file from URL. This is what's troubling me. Object object. Let's take a let's just take another look here. So import 1. I think I worked it out. No. That was quick. Goodness. It is an opt I did pass in an object, didn't I? But that's not what's being passed in in the in the reference. The import URL is a string and the body, which is a partial file that should have been passed in as a string. All you need to do, Kevin, is read. Read the docs. So read the There's another person here, and we still didn't get there. Yeah. Okay. Asset key. We got an asset key that time. Success. There should now be a file in the file library. Right? Correct. So we are loading it in. We are loading it in. Title, description, tags, file name disk, all grand, but Beautiful. What we also want to do is this. We want to, YOLO. I can't be bothered with all this. We'll just there we are. We'll take the asset key, then we'll read the new asset, and we'll return the data. So what we'll get back in in response to this is it will always import. So that's not ideal because if it already exists, we don't necessarily want to reimport it every time. But for the sake of this little demo, I think it's fine. Then we'll get the ID back. So now if I rebuild this and we run this again oh, it was still spinning up. What we should get back is the new file object indeed, including the ID. And the nice thing about this is, if I go to access control, public, and I turn on directors files actually, I don't even need to do that. If I go to httpsphzedm.ngrok.io, right, slash assets slash ID will get a you don't have permission to access this error. Right? But what I can do is go to the user, generate a token. Okay. Generate the token, save it, and I pin that to the URL. Nice. Access token equals. And there's our Hey. Merge directly, which is pretty which is pretty cool. That's very cool. To keep this easier, though, I'm actually going to change the permissions of the files collection. So then you net we never need to pass in, pass in the key. So public system collections, directors files, all access on. Great. So now that's public. So now we've done the endpoint. The job is to create the panel. So what we'll do here is npm run add. That should be pretty easy, I think. I say that. That's a dangerous Easy for you to say. He does it. I know. It's very easy for I I think you've got this, Kevin. I trust I believe. I trust in Bodie. Next week, when I'm when I'm driving again, things are gonna go terribly awry, and you're just gonna kick back and and relax and enjoy the experience. Oh, yeah. I know. Viewer. What did we call the previous attempt to an extension here? Snake GIF, find viewer, viewer. This is my custom panel. Now there is some text. Now what we need to do, I think what we'll do is we'll remove the options entirely, right, because we don't want any options. It'll be a standard panel. And inside of that panel, we are going to have button button. We no longer have text. That's not a prop that's being passed in anymore. We just deleted it. So what we'll do is we'll do a v button. No. We won't. We'll do a v let's talk about what all these v things are for a moment. Back in our docs here, back in our docs, we have, resources for building extensions, components. And we have this components playground. These are all of the directors components that are inside of the Data Studio are made available to extensions. So we have here v button, and this is the director's button inside of the director's app. So we we have an immediate availability of this v button component, which is, yeah, pretty, pretty nice. So we can access that straight away. So we can go v. I did just notice it was v button like this and we'll call this, not v. I keep going back to the button. Sorry. We're looking for No. That was right. No. Yeah. But we want the input because we want to put in the ID. Yes. Yes. Yes. Because we'll keep it easy. We'll we'll put, like, right now, manual ID of a game hit go Yeah. Rather than, Automating it. Yes. Pick pick it from a drop down because we're running we're running out of time. So v input to v model. So, what are we gonna do here? Export default. We're gonna do data return, and we'll call this ID. Like so. Then inside of here, we'll put in the v input with a model of ID. Then we'll say on key up dot enter fetch, game, GIF, get GIF, fetch, git. Cool. And we will add and this is not really an ideal way of of writing these, of course, but methods and we'll call this fetch GIF. Yeah. And we'll just console dot log or you know what? We'll alert with the this dot ID value. That's what we'll do for now just to test all of that works before we go too far. So we'll go in here. Oh, I need to sorry. Npm run build was the command there. Oh, error. Come on. What are you doing, error? What are you doing? After component panel component, we're missing a comma. Okay. NPM run build. Lovely. And we'll restart that. Also lovely. So let's refresh. Let's refresh. Just took a hot minute there. Insights. New panel. Viewer. No options available. Right? Because nothing happens to the panel itself. This, wonderful, is our panel and we'll say we'll save it. Hey, enter. Great. So this means we are able to do something with that. What we're gonna do with it is we're going to go back to our docs. We're gonna look at our composables for app extensions, which is what this is. I'm gonna just close out everything else for now. And we're gonna use API. And what this will do is it will allow us to make API calls, to our endpoints that are already authenticated. Though, to be fair, I believe this is a completely public endpoint, so it shouldn't matter much. But still, we'll do it as as it should be done. So what we're gonna do here is we are going to import use API. We are going to use this composable, like so, and then we can make our endpoints like and then we can make our calls like this. So we will make this async. And what we're gonna do is we're gonna post to slash importer/importer. And we have response dot data here. So all we're going to do is console dot log response dot data. Let's build. And this is all basically to get around that that security issue. Yeah. That course issue. I love that there's always just like a 6 step workaround to deal with course. Thank you. As opposed to just dealing with it head on. Yeah. So, we refreshed it. This is great. What's happened to my text box? What's happened to the text box? Is it no, but it's not. Where's my text box? Also, what's happened to this one? What's happening? Not seeing any errors. The API could not be found. Couldn't load extensions. Okeydokey. Have I missed a step? Have I rushed it again? Have I rushed a little bit? Oh, script setup. So we'll be using, we have to move this to script setup. And let's just take a quick look at how this I need to refactor this. Of course, we do, but that is fine. Async fetch GIF should just be able to, for the most part, come up here. We'll say, const fetch GIF equals a sync function like so. Then is that legit? Yeah. That is legit. And then so that's the methods. Then we have the data, which is the ID. So we will be import import ref from view. We'll create a new variable for that. We'll go const ID equals ref. Great. So we've handled that. Then we have props. And I believe the way to do props here is we say define props. Can we do this? I think we can do this. And all we care about here is show header, which to be honest, I do not care about. In theory, I've just refactored all of that. Let's take one more quick look, fetch GIF. So we have this dot ID. So we don't need that anymore. It'd be ID dot value, but we're not gonna alert it anyway. Kevin, watching your work is just fantastic, by the way. I love that I gave you this challenge and, like, everything has gone wrong. Okay. This is this is an Axios instance. I don't quite remember how it bloody works. So I think what we do here is we do, and I need to check, axios post. Oh, we do we literally just pass in the whole object as the second. Great. So in here, we're gonna do URL equals ID dot value. Okay. And then we have response dot data. Cool. So we'll console mock that. Alright. Let's rebuild that. Is it gonna complain this time? No, it bloody isn't. Docker Compose up. You give me a right workout here. Let's refresh, and we should now have access to use API. Now none of it's loading. Now it is. And there's our text box. Let's open up our dev tools. Of course, it didn't like the URL that was before. That was this panel here. Just testing that out. So in here Can I grab a screen? Pop it off at the side. Over here, yeah, we need a game ID. So we can do that here. Game ID. So we're not done yet because we're not gonna display it yet. If we hit enter, what's happened? Refused refused to load the images. No. No. No. That that's the old one. That's the old one. In fact, if I just remove that whole thing, we'll be better off for it. Let's refresh. See no errors. Let's paste that and let's hit enter. Nothing's happening. Why? Fetch GIF. Let's make sure this is ever running. You know what? This is all cute and all this. Let's just, just in case it's some silly async fetch gift, just in case it's some silly, async function, just in case it's something silly in the way that oh, I think it might have said invalid URL. I think it might. Do you wanna know why invalid URL? Because it isn't the full URL. We're just posting the ID. Oh, that's right. Yes. So what we'll do on the importer package is we're actually going to, we're gonna import req.body.url. Cool. So we're gonna make URL. Where is it? Here. Exported.battlesnake.comporter.battlesnake.games. And then here, req.body.idgamegame, maybe we call it. And in here, we pass in the earth. So now it needs a game. That's the value we need to pass going back to our panel. This is going to be game ID dot value. Let's rebuild the extension. Okay. Oh, yeah. We'll refresh. I do understand our network error showing there. Now we'll paste in the ID of the game. We'll hit enter. Oh, you know what we didn't do? We didn't open up dev tools. Let's skip to console. Wicked. Okay. We now have an ID of a directus I like, a directus asset in the ID. So all we need is the ID here. So this is the ID of the game, game ID, game ID, and then an image in here and the source of that image is going to be, how is this going to work? We need to just have a quick think about this. I'm gonna try something out. I think if we go /assets slash and then we need a value in here. So in here, we're gonna go image ID, and it's gonna be a ref and image ID dot value equals response dot data dot ID. Right? So Yep. Now this has the value in it of the ID, image ID. First thing we'll do here is just a quick v if image ID. We only wanna show it when that's the ID, and then here is going to be image ID. Let's see if that works. I'm not a 100% sure it will, but should do. I have faith oh, yep. There we go. I don't. Let's refresh. 1 of us has got 2. Great. ID is not defined. Why? Because ID dot value is gonna be game ID dot value. I'm feeling the heat, but I'm having a blast. I know. I was gonna say I feel like I gave you, like, the small challenge that became the much larger Yeah. But not not 145 in or whenever it was. That's not really the time to do it. But if we wait just a moment, nothing happens. Why? Console. Assets undefined 4 3. Fine. Let's figure out oh, image ID. Complete typo there. Remember this, Kevin. Remember this as the challenge comes next week. I'm having I'm having a blast. I really enjoyed last week, last episode. And now we have Hey. What is I? Kevin. Kevin. Kevin. Kevin. In an ideal world, you can obviously select from your existing games. It will handle all the logic for you. I think we're firmly out of time to do that this time around. Next time we and next time we won't be touching insights. But everyone, I hope you've had a wicked time. Any final remarks, Andrew? Thank you, Kevin, for taking on the challenge. Thank you all for watching. And be sure to tune in for our next episode of Ready. Ready. Set. Set. Battlesnake. Battlesnake. Nat, when you edit this, can you just time it so we get that right? Thank you. Bye.",[201,202],"6b42155e-93fc-4831-9905-6d258ea56f02","18c0d5b8-300f-454e-b49a-b7f3c1690936",[],{"id":134,"number":135,"show":122,"year":136,"episodes":205},[138,139,140,141],{"id":141,"slug":207,"vimeo_id":208,"description":209,"tile":210,"length":211,"resources":212,"people":216,"episode_number":131,"published":219,"title":220,"video_transcript_html":221,"video_transcript_text":222,"content":8,"seo":8,"status":130,"episode_people":223,"recommendations":226,"season":227},"toggle","925489400","In the season finale, Kevin and Andrew fly close to the sun to understand which snake strategies work best, by A/B testing live with DevCycle. Do they keep their wings?","b98c544a-5cc0-453f-bce6-d75614580ac3",105,[213],{"name":214,"url":215},"DevCycle","https://devcycle.com/",[217,218],{"name":151,"url":152},{"name":154,"url":155},"2024-05-02","Toggle","\u003Cp>Speaker 0: Hello, everybody.\u003C/p>\u003Cp>Speaker 1: Hello. I'm sorry, Kevin. Ready yet.\u003C/p>\u003Cp>Speaker 0: You are ready. Alright. Let's try this again. Alright. 3, 2, 1.\u003C/p>\u003Cp>Hello, everybody, and welcome to ready no. I\u003C/p>\u003Cp>Speaker 1: didn't know where to do that. We're gonna\u003C/p>\u003Cp>Speaker 0: do it. Ready. Ready. Set. Set.\u003C/p>\u003Cp>Battlesnake. I don't think we're gonna get it, Kevin. I don't think\u003C/p>\u003Cp>Speaker 1: we're gonna say the final episode of\u003C/p>\u003Cp>Speaker 0: Alright. We're gonna try this again. I feel like this is I feel like this is good for the final episode. Let's do this. For the final episode.\u003C/p>\u003Cp>Let's get this right. Welcome to the final episode of Ready set, set, battle mistake. Mistake. We're close enough. We got there.\u003C/p>\u003Cp>Speaker 1: Close enough.\u003C/p>\u003Cp>Speaker 0: We got there. I'm gonna count it. Alright. So welcome, everyone. Episode 4 of Ready, Set, Battlesnape.\u003C/p>\u003Cp>I'm Andrew. This person either down below or to my right or left. I don't know where he's he is right\u003C/p>\u003Cp>Speaker 1: now. Direction.\u003C/p>\u003Cp>Speaker 0: Is Kevin, and we are your your snake charmers, your code, your code coaches, your, your fun fans. I don't even know. But we are here today.\u003C/p>\u003Cp>Speaker 1: You are fully overestimating our entertainment value.\u003C/p>\u003Cp>Speaker 0: I I don't know what you're talking about. I feel like our entertainment value can never be underestimated. We are we are more entertaining than we even think. Thank you so much for coming back and checking this out, Kevin. Thank you so much for for coming back again after I kinda threw you, a little bit of a, a little bit of a challenge at the end of our our last episode together.\u003C/p>\u003Cp>Speaker 1: It was tough.\u003C/p>\u003Cp>Speaker 0: But we got there. I'm sure you'll return the favor to me today. Yeah. So if you haven't caught episodes 1, 2, and 3, you should probably go back and check those out. We, kind of we're gonna talk a little bit more about exactly what we've been doing, But, in episode 1, we built a Battlesnake, and we'll talk a little bit about Battlesnake kind of as we go through.\u003C/p>\u003Cp>Obviously, in episode 2, we implemented, that same logic from our 1st JavaScript starter snake in, Directus. In episode 3, we added a whole bunch more logic to our snake but mostly just to visualize the games that were coming through the platform. And today Kevin, do you know what we're doing today?\u003C/p>\u003Cp>Speaker 1: I I do, but I will let you I will let you share. I think it's your turn.\u003C/p>\u003Cp>Speaker 0: I think it is. I feel like you've been leading the show for the entire time. So it's probably my turn. Also, I feel like should I be let me see. I'm, like, wanting to be more centered.\u003C/p>\u003Cp>Oh, where are we? There we go. Now I'm feeling good.\u003C/p>\u003Cp>Speaker 1: Okay. Good.\u003C/p>\u003Cp>Speaker 0: Alright. I thought that, like, make make the choice to change where your camera is as you're as you're on on camera. Yeah. Screw it.\u003C/p>\u003Cp>Speaker 1: Why not?\u003C/p>\u003Cp>Speaker 0: Good vibes. Good vibes. So, on today's episode, we are going to be, implementing a new technology that we haven't really looked at, called Devcycle. And so, you now know from the last few episodes about what Battlesnake is. You know what Directus is because you're here on Directus TV.\u003C/p>\u003Cp>And so now is the time to introduce you to the technology that I work with on a daily basis as a developer advocate at Devcycle, which is a feature flag management platform. So our plan today is to try and add some logic to our, Directus snake to get this, working is maybe a relative term. Getting the the dev cycle platform yourself\u003C/p>\u003Cp>Speaker 1: a lot of scope there. A lot of scope. Working is a relative term.\u003C/p>\u003Cp>Speaker 0: But we're gonna use the dev cycle platform to, add a little bit of, add a little bit of flavor to our snake and give it a little bit of, give it a little bit of control. And that will make a lot more sense in a minute. But Kevin, you look like you have something to say.\u003C/p>\u003Cp>Speaker 1: I I mean, I'm gonna ask the question which I think you're probably gonna work towards, which is like, can we just talk for a moment about what feature flags are and then maybe what you had in mind, and then we launch straight in.\u003C/p>\u003Cp>Speaker 0: I love it. So, a feature flag, if you've never heard of them before, condition conditional statement in your code, that allows for you to, turn features on and off depending on a bunch of targeting criteria. So let's say you are working on a new feature on your platform, or a new feature maybe for your Battlesnake, and you're not quite ready to to roll that out in all of the games, you could add a feature flag. Just wrap it around the code and then through a feature management platform like DevCycle, you can say, for these kind of games, I want you to use kind of this logic. And for these kind of games, I want you to use these this logic.\u003C/p>\u003Cp>It's a really big part of things like trunk based development. So if you're moving from, like, branch based development where you're sort of, like, having features in branches that are long lived, and then we all had to deal with sort of, like, long lived branches and merging and dealing with merge conflicts. Mhmm. Feature flags are kind of a way to avoid those things just by constantly having your new features in code, and you can kind of decouple the code release from the feature release process.\u003C/p>\u003Cp>Speaker 1: Interesting. Okay. And just to embellish this with my understanding, and you can tell me if I'm wrong, they basically work. So you put these conditional statements in your code, and what triggers those is this feature management platform. And typically, you would do that with some form of either like AB testing where people get allotted into these random buckets or you can be a bit more pragmatic and say 5% of users are gonna have this conditional turned on and therefore get a new feature.\u003C/p>\u003Cp>And then we will also, like, we'll analyze their behavior as a result and decide whether it works, if people get it, and so on, rather than going it's an all or nothing kind of deal. Right?\u003C/p>\u003Cp>Speaker 0: You got it. You got it.\u003C/p>\u003Cp>Speaker 1: Got it.\u003C/p>\u003Cp>Speaker 0: That's it. Alright. Money. So Great. That is a very ambitious plan that I just laid out, by the way.\u003C/p>\u003Cp>I I feel as though may we get to a stage where we're able to, like, just determine, what kind of gameplay we're gonna have? That's possible. Maybe not in a 2 hour stream, but, we are gonna get Directus and Devcycle working together. How do I know they work together? Because I've already written a blog post on this separately from Battlesnake, so I got to explore.\u003C/p>\u003Cp>And so now is the time to use my knowledge for for good and for evil.\u003C/p>\u003Cp>Speaker 1: We'll link the blog post, by the way. We'll link the blog post. There are 2. There's 1 on our blog, that Esther wrote, which uses dev cycle, and 1 on the dev cycle blog, which you wrote, which is direct us. And I'll put a link to both of them in the resources section that accompany this video.\u003C/p>\u003Cp>Speaker 0: Yes. I love it. So, y'all are seeing my screen right now. And so up here, you'll notice that this looks very similar, to, 1, mister Kevin Lewis's screen, although not in the Arc browser. I'm not apparently a strong enough, software developer to use Arc.\u003C/p>\u003Cp>Not there yet. Once it comes to Windows, I'll start using it. It's it's it's happening. I know it's coming. I'm in a month of this.\u003C/p>\u003Cp>Speaker 1: And then people will full on get this, but we're clearly recording this a day after the last one because they these are the games from the last episode they created about 23 3 hours ago, which is completely legit. We did something a little cheeky before this stream. We in a completely low rim fashion, you're running a director's to project. And I literally was like, here's a sequel database file and here's the extensions. Go run them and here's the creds I used to go log in.\u003C/p>\u003Cp>We may have to reconcile with that at some point when things break. So there is a chance things won't behave and we'll have to fix that on the fly, but should be okay.\u003C/p>\u003Cp>Speaker 0: I love it. So we have our snake our direct to snake here. It's alive. It works. Not yet.\u003C/p>\u003Cp>We haven't determined if it works yet, which we probably will have to do. I also have our snake that was running on Replit. So the original, ready set battle snake that we created from the JavaScript starter snake. So Mhmm. I have that snake, but Replit was getting real mad at me today because I was using too much RAM and and compute.\u003C/p>\u003Cp>So I pulled that down, and put it into this wonderful, repo here. Yes, sir.\u003C/p>\u003Cp>Speaker 1: And you're gonna be bumping your font size up about 5 times.\u003C/p>\u003Cp>Speaker 0: No. We're gonna expect everyone to to zoom in. Exactly. Exactly. That's the look of the look at\u003C/p>\u003Cp>Speaker 1: my spectacles.\u003C/p>\u003Cp>Speaker 0: Everyone has. And so, yeah, let's let's zoom in here a little bit. Alright. So this is the the kind of the original snake that our code is based off. So you will notice here that not all the logic that we implemented over the past couple of episodes are in here.\u003C/p>\u003Cp>That's fine. This is more just to, like, show us how it's gonna work once we get there, and then we're gonna implement this indirect disk. We're gonna try and implement it in direct disk. But I think maybe the first thing we should do, Kevin, is actually get our Directus snake working. What do you think?\u003C/p>\u003Cp>I feel like that's a logical first step.\u003C/p>\u003Cp>Speaker 1: And what we're gonna do to make that happen is we're gonna set up ngrok. And so do you pay for ngrok?\u003C/p>\u003Cp>Speaker 0: I don't, but I have ngrok. So, like, I can I can set it up right now, but it'll be ephemeral?\u003C/p>\u003Cp>Speaker 1: Incredibly heavily, like, rate limit to you. Let's pause here. Let's get NGROC set up and using temporarily my auth token, and then we will come back. Great. So this bit, we won't we won't publish.\u003C/p>\u003Cp>But, yeah, it was actually the one thing we forgot before we started. So let me just figure that out.\u003C/p>\u003Cp>Speaker 0: Yeah. Throw me the key and then I can,\u003C/p>\u003Cp>Speaker 1: Indeed.\u003C/p>\u003Cp>Speaker 0: Alright. And let me just go to engrok now because I was actually logged in earlier. It was working for me in the games that I was playing, but,\u003C/p>\u003Cp>Speaker 1: it but then it gets mad. That's the whole problem here. So what we want here is your auth token. So, here is the engrok command you are going to want to run here in the chat. Yep.\u003C/p>\u003Cp>Speaker 0: Sorry. I'm just\u003C/p>\u003Cp>Speaker 1: then because I'm on, like, old, the command looks slightly different, and that's there.\u003C/p>\u003Cp>Speaker 0: Where are you hiding? Hold on. My windows have all disappeared. Where are you, Kevin? There you are, in chat here.\u003C/p>\u003Cp>Alright. Wonderful. There's a chat. Here's a command. We're gonna add this auth token.\u003C/p>\u003Cp>Beautiful. And we're gonna head back over here to our terminal and oh, nice. There we go. Paste. Beautiful.\u003C/p>\u003Cp>Auth token saved, and let's run\u003C/p>\u003Cp>Speaker 1: Nice. This.\u003C/p>\u003Cp>Speaker 0: It's good. I set up ng croc earlier.\u003C/p>\u003Cp>Speaker 1: I knew Yeah. Yeah. Absolutely.\u003C/p>\u003Cp>Speaker 0: Beautiful. Alright.\u003C/p>\u003Cp>Speaker 1: Good. So I think we can actually snap back now, and Yep. It's up to you what you want on the screen. We could be here if you want.\u003C/p>\u003Cp>Speaker 0: Yeah. I mean, I mean, if you've never used ngrok before, I mean, we talked about this in episode, I guess, 2. We were looking at this. I have the free version. Kevin's got one that's less rate limited, so we're using that one.\u003C/p>\u003Cp>But it's super easy to install. Like, just homebrew it, and then you add your auth token to ensure things are happening. But, anyways, we've got it running right now. And then my direct to snake over here is should technically be working. Now, Kevin, I'm really curious.\u003C/p>\u003Cp>You should technically be able to go and run a game on yours, and see if it works. And I'm interested I think\u003C/p>\u003Cp>Speaker 1: Are you running directors?\u003C/p>\u003Cp>Speaker 0: Apparently, I am not. I think I might have closed it. Nope. I'm running it. Yep.\u003C/p>\u003Cp>We're all good. Where is it running though? That's the real oh, you know it's running in another one.\u003C/p>\u003Cp>Speaker 1: Do you have another\u003C/p>\u003Cp>Speaker 0: I do.\u003C/p>\u003Cp>Speaker 1: Versus code do. Window. There it is. Awesome. So we're gonna watch\u003C/p>\u003Cp>Speaker 0: these logs here. We're gonna bump Shall\u003C/p>\u003Cp>Speaker 1: we whack the font size up there as well? So I'll add ready set battlesnake and hungry bot once again to an 11 by 11 board. I'll hit start game.\u003C/p>\u003Cp>Speaker 0: Yeah. And there we go. It's coming through. Awesome. We're working.\u003C/p>\u003Cp>And you're like, but Andrew and Kevin, we can't see the games, and therefore, this isn't fun and enjoyable for us. Us. Fear not. Because I think actually an easy way to do this so we can keep ready, set, battlesnake in your repo. Can you make ready, set, battles cake public so that I can add him to my Yeah.\u003C/p>\u003Cp>Speaker 1: I can. Yeah. My battlesnakes. Ready set battles cake. We named it that.\u003C/p>\u003Cp>So it had a k instead of an n because it was the Kevin version of the snake as an FYI, and it is now public.\u003C/p>\u003Cp>Speaker 0: Awesome. And so if you're ever in the Battlesnake platform, really easy. All you gotta do is just search for snake. Ready, set. Should find him skate.\u003C/p>\u003Cp>There we go. That's what we want it to look like. And so now we've got them in there. And now we're gonna see this game going on, and it should be all of our logic and direct us. Something's not happening.\u003C/p>\u003Cp>Something's unhappy here. Let's see what it is.\u003C/p>\u003Cp>Speaker 1: Womp womp.\u003C/p>\u003Cp>Speaker 0: I love that we tried and then we failed.\u003C/p>\u003Cp>Speaker 1: Could you remove the what the old one password, pop up?\u003C/p>\u003Cp>Speaker 0: I certainly can. Okay. There we go. Oh, no. Nope.\u003C/p>\u003Cp>Same thing happened again, actually. Create Rematch.\u003C/p>\u003Cp>Speaker 1: So let's think it through. What is going wrong here? It could be that our flow isn't running correctly, but it should be. Yeah. Like, it's going off.\u003C/p>\u003Cp>It's triggering the flows. It's getting a 200, which means it's getting a response, and it's returning. So what is going what is going on?\u003C/p>\u003Cp>Speaker 0: What is going on? We haven't gotten Because\u003C/p>\u003Cp>Speaker 1: it was randomly breaking last time too. At the end of session 2, at the end of the second episode, our snake was, like, overpowered. Like, it was so good, and then it started to crumble. It started to grumble, last time.\u003C/p>\u003Cp>Speaker 0: Let's take a look at our logs. We have got some logs in here. Holy moly.\u003C/p>\u003Cp>Speaker 1: We do.\u003C/p>\u003Cp>Speaker 0: Let's look here. So is there anything going on in here? We've got move now. Original. Yeah.\u003C/p>\u003Cp>So I think we've got something going on there. Some so what do we have here? Yes. Feeding a lot of things there. False, false, false, false.\u003C/p>\u003Cp>Oh, no safe moves. Avoid head to head. I think it might be in there. False, false, false, true, true. Yeah.\u003C/p>\u003Cp>I think the issue is in\u003C/p>\u003Cp>Speaker 1: Which we've already determined that we think is the case. So you know what we'll do to keep ourselves moving? If you close the pane, why don't we just avoid that entire operation and just take avoid body straight to determine move? Avoid head to head is a, you have to just hit the little edit in the top right of the Oh,\u003C/p>\u003Cp>Speaker 0: of course, we do see.\u003C/p>\u003Cp>Speaker 1: Yeah. And hopefully yeah. If we just do that and hit save, yeah, just yeah.\u003C/p>\u003Cp>Speaker 0: Oh, we're back. We are back. We're back. It's alright. We're there.\u003C/p>\u003Cp>I have a belief that if you just hit the save button enough times, we're gonna be able to resolve.\u003C/p>\u003Cp>Speaker 1: I'm pretty sure that's the definition of insanity. I\u003C/p>\u003Cp>Speaker 0: think it is too. We're gonna switch things up for just a minute. Yeah.\u003C/p>\u003Cp>Speaker 1: And save\u003C/p>\u003Cp>Speaker 0: this, and that didn't work. And then we're going to go back over here. And then we're going to go back here. And we're going to see if that worked. Oh, disk.\u003C/p>\u003Cp>It was a bug.\u003C/p>\u003Cp>Speaker 1: It was a bug, by the way. And it had an issue has been opened on this bug. So there you go. Maybe just discard the changes and just we can have\u003C/p>\u003Cp>Speaker 0: Alright.\u003C/p>\u003Cp>Speaker 1: Let's see\u003C/p>\u003Cp>Speaker 0: if we can create another game, see if this will do it. I mean Okay. The oh, nope. Still doing the same thing. Still so moving out of bounds, logic is there.\u003C/p>\u003Cp>It's definitely happening in 2 minutes ago. Let's get You\u003C/p>\u003Cp>Speaker 1: have to refresh. Sorry.\u003C/p>\u003Cp>Speaker 0: Alright. So let's go here, and let's see again. So determine move is still where things are, yeah, getting hung up.\u003C/p>\u003Cp>Speaker 1: Oh, and you're saying avoid head to head is where everything went to force when they're logically, it should not been happening.\u003C/p>\u003Cp>Speaker 0: I think so. Maybe that's what it is. Let's go avoid head to head options. There we go. False, false, false, false.\u003C/p>\u003Cp>Payload. Avoid bodies.\u003C/p>\u003Cp>Speaker 1: Okay. So so you know what we're gonna do? We're gonna jump into avoid head to head, and we're just gonna return the inbound date. Like, we're not gonna do anything.\u003C/p>\u003Cp>Speaker 0: Alright. Let's go in there and do that. So here we go.\u003C/p>\u003Cp>Speaker 1: Right. Because that's clearly where it were. Yeah. We're gonna literally delete everything apart from line 2 and 30. So everything in the middle there, ditch it off.\u003C/p>\u003Cp>Doesn't matter.\u003C/p>\u003Cp>Speaker 0: Can we not just oh, we can't comment. Really?\u003C/p>\u003Cp>Speaker 1: I think no. So just but it's fine. We don't need it. This is this is the last episode. There we go.\u003C/p>\u003Cp>I also have a copy of it, so it's fine.\u003C/p>\u003Cp>Speaker 0: Ah, success. Alright. So let's save this and see if it works. There we go. And now let's go and try and recreate a game again.\u003C/p>\u003Cp>Speaker 1: Because if that's the step that was causing grief.\u003C/p>\u003Cp>Speaker 0: It was. Yep. Do\u003C/p>\u003Cp>Speaker 1: you remember when I wrote all that really elegant code and it worked fantastically? Turns out not so much.\u003C/p>\u003Cp>Speaker 0: Well, it's alright. It worked fantastically until it didn't. And that is the key to all the programming. It's all good until, you crash production.\u003C/p>\u003Cp>Speaker 1: Okay. That makes me feel better that we've identified the issue and removed it for now.\u003C/p>\u003Cp>Speaker 0: Okay. So ready, set, battlescape is working, and this is really the key here. It's still doing the job that it needs to do. So we're gonna make, we're gonna do something a little bit different now. We are going to actually be, having we're gonna bring ready set battlesnake back from the dead, in a new world.\u003C/p>\u003Cp>So let's, let's kinda go over here. So this is good. Directus is working, and now we're gonna we're gonna ignore that Directus even exists for the next little while. And we're actually gonna head back over to\u003C/p>\u003Cp>Speaker 1: Who needs to pay rent?\u003C/p>\u003Cp>Speaker 0: Who needs to pay rent? Not me today. And I wanna look at, wanna look at our code here. And so is this okay for you, Kevin, or is it still too small?\u003C/p>\u003Cp>Speaker 1: Yeah. Maybe take up another yeah. Maybe that maybe that's good, I think, for people watching.\u003C/p>\u003Cp>Speaker 0: Awesome. So in here, not a lot has changed from when we were dealing in replit. So we still have our server dotjs, which is just an express server. We've got the 4 endpoints, the sort of root endpoint, start, move, end, and then we've got a bunch of stuff in here that's kind of, yeah, talking about what it's returning. We've got a read me here, which explains just how this works, which we don't really care about right now.\u003C/p>\u003Cp>And then we've got our package. Json. I haven't added anything new to here except for this new dependency, which is the dev cycle, SDK. And so this is necessary for us to implement dev cycle. If we want to do it this way, technically, we could do all of this via our management and bucketing APIs, but I think it's cleaner through the through the SDK.\u003C/p>\u003Cp>And then, there's some small changes. There's another file that's here, and we'll maybe call this in just a second. And I'll explain how I got to this file, which was not fun this morning when I was prepping for the show. So, originally, in here, we had our run server. We had our info endpoint.\u003C/p>\u003Cp>We had our start and end endpoints. And then we had this move endpoint, which actually had some really good logic inside, that if we go and ignore all of this stuff, here was our original logic. And I'm gonna uncomment all of this stuff. Oh, I'm gonna try and uncomment all of this stuff if I actually uncomment the right section. There we go.\u003C/p>\u003Cp>So this was our original logic. And I'm actually think just to make sure that this is working because, yeah, I've changed this so it wasn't async now, which was part of my issue earlier. I'm gonna save this. So this was sort of our dumb snake that we first started with. So Not dumb.\u003C/p>\u003Cp>Yeah. But, like, dumber. And so you can see here all of our steps have kind of been implemented here. The base steps here, same thing that's going on over in Directus. Disk.\u003C/p>\u003Cp>So let's see if this is working. I'm gonna run this express server. I think it's node index dotjs. So this is gonna run\u003C/p>\u003Cp>Speaker 1: How are you gonna expose this as a Battlesnake if we're already running ngrok for directors?\u003C/p>\u003Cp>Speaker 0: Oh my good guy. Also, updated cache direction to down. It is constantly running that caching. Interesting. That's fun.\u003C/p>\u003Cp>Speaker 1: So the caching wasn't originally there, Andrew. So Wasn't. And please tell us about the cursed file.\u003C/p>\u003Cp>Speaker 0: So we're gonna ignore the cursed file for a minute, and then we'll get to the cursed file. But let's see if we run this right now. Technically, I should be able to run 2, right, through engronk?\u003C/p>\u003Cp>Speaker 1: I'm not convinced, but we'll we'll find out.\u003C/p>\u003Cp>Speaker 0: Okay. What port does Directus run on?\u003C/p>\u003Cp>Speaker 1: Not that one.\u003C/p>\u003Cp>Speaker 0: K. Well, let's see if this works. No. It works. It's there.\u003C/p>\u003Cp>Speaker 1: No. No. No.\u003C/p>\u003Cp>Speaker 0: No. No. No. No. No.\u003C/p>\u003Cp>No. No. No.\u003C/p>\u003Cp>Speaker 1: Locally, but you still have to expose it.\u003C/p>\u003Cp>Speaker 0: Yes. So can you see if one shows up in your, is it list it'll show up on your ngrok dashboard on your end if it's running.\u003C/p>\u003Cp>Speaker 1: I didn't even know there was an ngrok dash I didn't even know there was an ngrok dashboard.\u003C/p>\u003Cp>Speaker 0: We'll see. But yeah.\u003C/p>\u003Cp>Speaker 1: Let's take a look.\u003C/p>\u003Cp>Speaker 0: So behind the scenes, I'll actually pull this up just so people can see what this looks like. So when you go to the ngrok dashboard and you're logged in, if I am logged in, so you have, like, this authentication and set up stuff. And then you also have this tunnel section. And in the tunnels, I think it's tunnels where it actually shows you, yeah, what are currently online in your account. Account.\u003C/p>\u003Cp>And so you should see you should see\u003C/p>\u003Cp>Speaker 1: I've got one.\u003C/p>\u003Cp>Speaker 0: You've got one? K. So this is not doing what we wanted to do. Wait.\u003C/p>\u003Cp>Speaker 1: Wait. Wait. Wait. No. No.\u003C/p>\u003Cp>Yeah. And the one is you. Oh, it is? Yeah. Started 10 minutes ago.\u003C/p>\u003Cp>I'm not running anything locally.\u003C/p>\u003Cp>Speaker 0: That's that's Directus, though. That's why.\u003C/p>\u003Cp>Speaker 1: That's directives.\u003C/p>\u003Cp>Speaker 0: Okay. So here's what we're gonna do. And now we need\u003C/p>\u003Cp>Speaker 1: to try and run it a second time, but I'm not convinced.\u003C/p>\u003Cp>Speaker 0: We can shut down directives for right now because we don't need it as we're just kinda demonstrating how this works in the index dot JS. So we're gonna shut down Directus for a second, and then we're gonna run it over on our other one. Alright. So we've shut down Directus, and let's go back over to index dot j s. Let's cancel this out and awesome.\u003C/p>\u003Cp>Restarted that. What is the address that I'm wanting to go to, Kevin? Can you paste it somewhere for me?\u003C/p>\u003Cp>Speaker 1: I don't understand what you're asking.\u003C/p>\u003Cp>Speaker 0: Never mind.\u003C/p>\u003Cp>Speaker 1: I But, also\u003C/p>\u003Cp>Speaker 0: yeah. I've But you're not running ngrok yet. Right? Oh, that's right. I have to rerun.\u003C/p>\u003Cp>No. I am running it.\u003C/p>\u003Cp>Speaker 1: You need to rerun it.\u003C/p>\u003Cp>Speaker 0: Exactly. Rerun ngrok. Yeah. Oh, there we go.\u003C/p>\u003Cp>Speaker 1: Got it.\u003C/p>\u003Cp>Speaker 0: Bad things happening. Okay. Let's go\u003C/p>\u003Cp>Speaker 1: But this time, not 8,055. Yeah. Yeah.\u003C/p>\u003Cp>Speaker 0: 8000. Alright. Here we go. Beautiful. Alright.\u003C/p>\u003Cp>Alright. So let's go over here and see if this is working. Beautiful. Alright. So this is running now.\u003C/p>\u003Cp>So if I go back over here to account settings oh, not account settings. My battle snakes. I'm gonna go ready set battlesnake, and we're gonna leave a JavaScript, and we're gonna have this on nothing, no platform today, and then we're gonna change our server URL. Yes. Like that.\u003C/p>\u003Cp>And now if I go in and add ready set set, Battlesnake to a game, create game. And ready to set Battlesnake.\u003C/p>\u003Cp>Speaker 1: Remove the Octa thing in the top right? It's very annoying.\u003C/p>\u003Cp>Speaker 0: My goodness. All of the prompts. And then we are going to see if this runs. Okay. Good.\u003C/p>\u003Cp>Basic logic, not running into himself, not running into walls. Good. So he's working. Alright. But I haven't implemented the logic for dev cycle yet.\u003C/p>\u003Cp>So let's go look at what's going on. So we've got the moves that are feeding through there. That's awesome. That's what we want. I'm gonna actually turn this off for a second, and then turn it back on.\u003C/p>\u003Cp>No. I'm not gonna turn it back on because it's gonna constantly need to be caching. Okay. So, that's our basic code. Right?\u003C/p>\u003Cp>We're good. We're there. We're not here for basic code today. We are here for confusing broken code that will cause many problems and will make our heads hurt.\u003C/p>\u003Cp>Speaker 1: You're really teeing this up, and I'm horrified before we've started.\u003C/p>\u003Cp>Speaker 0: Don't worry. You and me both. So let's look at the new logic that I have in here, which is not great logic, but, I'll explain it. So, what we're doing is every time the move endpoint is called, this move function is called. Yes?\u003C/p>\u003Cp>Everybody should track that so far. So what I'm doing here is I am calling a, this get cache direction function, which is basically every second or millisecond. I'm not sure. I think it might be millisecond. It's calling the dev cycle, platform and asking what move should I be getting for whatever user is on the platform right now.\u003C/p>\u003Cp>This will all make sense when you kinda see the logic, but, basically, it's it's pulling the dev cycle platform to ask it what move you should be making as a snake. Does that make sense? Yeah. I think your face\u003C/p>\u003Cp>Speaker 1: Unfortunately, this isn't even remotely the direction I thought we could go in.\u003C/p>\u003Cp>Speaker 0: No. Don't worry. We're not gonna we're not gonna manually we're not gonna manually do this. We're I just wanna demonstrate the, like, how it actually functions. And so this is good.\u003C/p>\u003Cp>Speaker 1: Mhmm.\u003C/p>\u003Cp>Speaker 0: And I actually you can do it mid game too as long as your your click game is is on point. Oh, man. Which we can do today because there's 2 of us here. So Oh my god. We're gonna go and actually look in this cache dotjs file.\u003C/p>\u003Cp>So here's our cache.js file. Couple things happening here. First thing, we're importing the server SDK that this dev cycle from there. Then we're initializing our dev cycle, client in here, despite it being a server application, we're calling it a dev cycle client. This stuff is That's fine.\u003C/p>\u003Cp>That makes sense. Yeah. This stuff isn't super important, the cloud bucketing and enable EdgeDB. It is and isn't, but really the important thing here is that we're initializing the dev cycle client. We're creating this cached direction, which is setting the default move for our snake to go down.\u003C/p>\u003Cp>So, basically, if, the first time, if there's been no caching, if this has never run before, it's gonna go down. And then, yeah, within this function, we're feeding in the user variable. So you have to feed this in every single time that you, that you do anything with the external platform. You have to feed to the user. You can feed in custom properties, which we may try and do today, where you can, like, base it on different information that you may have.\u003C/p>\u003Cp>But this is the idea here. And then once we've got this dev cycle, client established, we're then going to look for a specific variable that exists in the Dev cycle platform. Think of a variable as, like, the feature flag that you're looking to pull. So whether you're wanting to see on or off, we're actually pulling a string. So we're pulling for the direction for this user.\u003C/p>\u003Cp>What should this user be seeing if we're looking for the direction variable that's in there? And then we've got if we do end up having a preferred direction. Yeah. Because where's my preferred direction defined? I think it was defined over here.\u003C/p>\u003Cp>Right? Yes. There we go. So, preferred direction. So, basically, it's saying that if there is the DevSecLE client being established and it finds that that direction variable exists, then we're gonna look for that preferred direction variable that we had defined over an index.js.\u003C/p>\u003Cp>And if it returns anything but none, it's going to put a new cache direction value in there, and then it's gonna log that it's updated the cache direction. Otherwise, it's gonna give you an error. And then if we go down here, yeah. We're basically updating this every 10 it's not ten seconds. This is, like, every 10 milliseconds.\u003C/p>\u003Cp>Speaker 1: That's very, very often. Very often. So a1000 is one second.\u003C/p>\u003Cp>Speaker 0: Yep. And then I gotcha. Yeah. So this is our code that we're doing here. Basically, we're just constantly pulling the dev cycle platform to, like, get the latest updated stage.\u003C/p>\u003Cp>So there's a little bit of a delay here, but it works pretty well.\u003C/p>\u003Cp>Speaker 1: I will believe it when I see it. Gonna be honest, mate. Okay.\u003C/p>\u003Cp>Speaker 0: Alright. You're like, this is garbage spaghetti code. What are you doing, Andrew? It's not like it's not the code.\u003C/p>\u003Cp>Speaker 1: It's just this isn't how I would have. Can I give you maybe an idea of how else\u003C/p>\u003Cp>Speaker 0: we could have implemented this sort of code? Yeah. Let's yeah. Let's go.\u003C/p>\u003Cp>Speaker 1: We literally just have, like, branching logic in the flow. And based on, like, there's the snake gets an ID for every game. So you can enroll them and then, like, 10% of the time or whatever, they get thrown into, like, strategy 2. And strategy 2 could be it become it just goes left all the time. Like, it doesn't matter.\u003C/p>\u003Cp>Yeah. But you could implement branching logic. Right?\u003C/p>\u003Cp>Speaker 0: So we are and that is what we are gonna do. So you're not wrong with that. I just wanna show how this works.\u003C/p>\u003Cp>Speaker 1: That's fine. Okay.\u003C/p>\u003Cp>Speaker 0: Then we can get into the logic. So Okay. That's our logic here. So, basically, we're calling this every 10 seconds, and then it's gonna if it's updating, it's gonna show us this using cache direction, and then it's gonna do that.\u003C/p>\u003Cp>Speaker 1: And just to clarify, the whole reason you've created this notion of a cache direction is so we're not gonna go to dev cycle every single move. We will go a 100 times a second instead.\u003C/p>\u003Cp>Speaker 0: So what's what's interesting is because we're basically running an async method here, there's a delay and devs battle sync doesn't like there being any sort of a delay. So I had to I completely got it. So you\u003C/p>\u003Cp>Speaker 1: have to have some I got it.\u003C/p>\u003Cp>Speaker 0: Yeah. You've gotta do the polling yourself. I think you probably if I was a better programmer, I could probably figure it out, but this was like No.\u003C/p>\u003Cp>Speaker 1: No. I I I get it now because you could call dev cycle every time, but you're not gonna be able to respond within 500 milliseconds confidently. Yes. So for that reason, instead, we will catch the last direction it gave you. Exactly.\u003C/p>\u003Cp>I got it. I got it. That makes sense. Fine. You're I'm with you.\u003C/p>\u003Cp>And\u003C/p>\u003Cp>Speaker 0: it's interesting. The reason I figured out that this was working is so if you notice here, in the logs, so we actually it returns move 117 right. What I was seeing in my logs was it would be like move 01746328. And so it was taking time to actually do the the the calculation. So anyways\u003C/p>\u003Cp>Speaker 1: Which is no good for the snake. It just won't work. Yeah. And so\u003C/p>\u003Cp>Speaker 0: I'm gonna leave this I'm gonna leave this at, at 10 milliseconds for right now, now only because I'm running on my own device, and I think your end grunt won't get upset at me. And because I know that it will demonstrate how this works. The downside here is I actually, Kevin, will need you to be the one that is starting no. Because nobody's gonna be able to see it. Okay.\u003C/p>\u003Cp>We're gonna see how fast Andrew can click things is what we're actually gonna see. So we're gonna start this again. So if everything's working properly over here, I've uncom oh, I've gotta uncomment this. Let me cancel that again. Let's uncomment that import statement.\u003C/p>\u003Cp>I think all the rest of this logic has already been commented. Awesome. So now if we run this yeah. So we're basically checking this, like, every 10 milliseconds. It's kind of crazy.\u003C/p>\u003Cp>I don't fully know why I don't think it's running every time it shouldn't be.\u003C/p>\u003Cp>Speaker 1: I'm unsure logging every time it's making your terminal choke.\u003C/p>\u003Cp>Speaker 0: Here's my question though, is we're not calling move all the time. So I'm confused why\u003C/p>\u003Cp>Speaker 1: That's a good point.\u003C/p>\u003Cp>Speaker 0: Get cache direction is being called every single time because it shouldn't be. It should just, like, literally only be called when moved unless I've got a game running somewhere. Do I have a game running somewhere?\u003C/p>\u003Cp>Speaker 1: You shouldn't be. No. I don't even know how you would check that.\u003C/p>\u003Cp>Speaker 0: Yeah. I don't know either. But, like, if I look at this okay. Request made.\u003C/p>\u003Cp>Speaker 1: Oh, are you the squiggly brackets? Yes. The destructuring.\u003C/p>\u003Cp>Speaker 0: I shouldn't have done that. Alright. So, yeah, it should not be running. It should be running every time. What is going on here?\u003C/p>\u003Cp>It must be something in this actual cache file. Let's see what's going on here. Import dev cycle, initialize dev cycle, let cash direction go down, update cash direction, import dev cycle.\u003C/p>\u003Cp>Speaker 1: Yeah. Because you're set into it here on line 33. So that's happening regardless. That's happening the moment it's imported. That's not linked to that function running.\u003C/p>\u003Cp>I don't think it's that much of an issue.\u003C/p>\u003Cp>Speaker 0: I'm gonna change this to, like, a 100 because what's that?\u003C/p>\u003Cp>Speaker 1: That would've that was gonna be my only suggestion is maybe just knock it knock it down a bit. Okay.\u003C/p>\u003Cp>Speaker 0: Yeah. Anyways, we're gonna do this. We're not gonna do a lot of this, because it's gonna be less important. But, we're gonna set this running, and then we're gonna go and actually take a look at the game. So here we go.\u003C/p>\u003Cp>So we're basically changing this direction, and I'll actually actually, let me cancel that for a second. And I'll go to the dev cycle platform so I can show you. I already set this up.\u003C/p>\u003Cp>Speaker 1: Yeah. Yeah. Yeah. That'd be good.\u003C/p>\u003Cp>Speaker 0: Here are the dev cycle form. Once I log in once it logs in come on, dev cycle. What are you doing? Oh, there we go. Logged into the wrong one.\u003C/p>\u003Cp>So we actually just launched a, a bunch of new example apps, and so I've been playing around with those. But there's no way to log out without, getting rid of the the demo mode. So, that's how we're gonna log out.\u003C/p>\u003Cp>Speaker 1: I feel like it's like a user retention is great if the user can ever log out, get a life hack.\u003C/p>\u003Cp>Speaker 0: All right. Let's close these again, see if I close this. And let's try app.devcycle.com again. And I think this is gonna load. Yeah.\u003C/p>\u003Cp>There we go. Awesome. Log in with my work count. It's gonna bring us over to the dev cycle platform, all of our production stuff. We're not gonna mess with our production stuff today.\u003C/p>\u003Cp>Speaker 1: Do it. Do it. Let your intrusive thoughts win.\u003C/p>\u003Cp>Speaker 0: So I did it at, at KubeCon. I actually we made a bunch of live changes while we were doing demos, and our entire engineering team got very unhappy with us. And so I think we're ready to set Battlesnake. And I've got this features area here. So I've got a feature that I've set up called my choose move feature.\u003C/p>\u003Cp>And this is Could you,\u003C/p>\u003Cp>Speaker 1: zoom in, sorry, 1 or 2 times? No. That's okay. The small text is quite small. We're good.\u003C/p>\u003Cp>Nice. Thank\u003C/p>\u003Cp>Speaker 0: you. Perfect. Yeah. So we've got our choose move feature. This isn't super important for us right now because I've only got one feature.\u003C/p>\u003Cp>And then I've got this direction variable that I have there, and I've defined 4 different variations, left, right, up, and down. These are string variables that are in there. And then in my targeting logic, I'm basically saying who to serve what or what to serve to what users that are feeding into that SDK. So here, I'm basically saying serve all of the users down, but that's also my default. So I actually want to be serving to see if this is working and actually talking right.\u003C/p>\u003Cp>I'm going to change this to serve everything right. And if this is working now, when I turn on that Battlesake server, I should be seeing everything in this updated cache direction except for the first one showing as right. So let's see if that works. There we go. And even the first one did.\u003C/p>\u003Cp>So we've got the changes happening here. And now if I go back over to the platform, change this to up and save and then take a look at my logs. And so it's like real time changes that are happening in there that you're able to kind of differentiate what's going on. So the interesting thing here is that, technically, platform to control your snake in real time. It's a terrible idea.\u003C/p>\u003Cp>Speaker 1: That is literally the like, like, we've seen people play Battlesnake live using, like, games controllers. And even that, you're like, well, there are some questionable choices here. This is like drop down and then save.\u003C/p>\u003Cp>Speaker 0: So but I'll show you something inter I'll see if I can get this to work, and I'll just do a I started using, there we go. So I started using, rectangle, and\u003C/p>\u003Cp>Speaker 1: Sure.\u003C/p>\u003Cp>Speaker 0: I can't believe that I've never used it, anything like that. Oh, yeah.\u003C/p>\u003Cp>Speaker 1: I use Raycast for it, and I have keyboard shortcuts. Oh, look at you. Which is really, really nice.\u003C/p>\u003Cp>Speaker 0: So let's grab ready set battle sneak in here. And we're gonna go start gaming. Just ready set battle in here. And so you'll see it's not gonna work right now because it's not running. So he's going up.\u003C/p>\u003Cp>So we know it's not down. There's no conversations that are happening right there. So since we know that we've set our game mode to, let's change it to left so we know that something's working here, and then I'm gonna start my server. And now if I create a rematch, should go left. Awesome.\u003C/p>\u003Cp>So we know that it's working. Sure. Alright. So now I'm gonna do the thing that is, like, the test of Andrew's ability to click things really fast. I'm gonna create a rematch.\u003C/p>\u003Cp>And then in, like, real time, I'm gonna click save. And there should be a switch if the board is big enough. But, actually, I need a bigger board because that will give me more time for things to go wrong. Let's go 19 by 19. Add, ready, set, Battlesnake.\u003C/p>\u003Cp>Start game. Oh, I shouldn't have done that. Anyways, it's fine.\u003C/p>\u003Cp>Speaker 1: You've done that. You you moved moved.\u003C/p>\u003Cp>Speaker 0: Do do do do do. No. It's too late. The game already runs as soon as you click create rematch, so I gotta be a super fast. Alright.\u003C/p>\u003Cp>Here we go. So right now, he's set to go left. I wanna change it so he goes\u003C/p>\u003Cp>Speaker 1: Don't worry about hitting play. Just hit create rematch, hit save, and we can see it in the playback that it's happened.\u003C/p>\u003Cp>Speaker 0: Alright. Here we go. There we go. Oh, and he's going left first. So literally, the worst example\u003C/p>\u003Cp>Speaker 1: that ain't gonna work. Before you create rematch before, you wanna yeah. Prime. Prime. Oh, yeah.\u003C/p>\u003Cp>Change it.\u003C/p>\u003Cp>Speaker 0: So right now, we're going down. So we're gonna get it. So we're gonna go first, we'll be going down, and then we should go right as long as we know where it, as we long as we know where it spawns. Okay. This should work, actually.\u003C/p>\u003Cp>This should be good. So here we go. Should go down first, and then at a certain point, go right. Oh, not fast enough. We're gonna try this again.\u003C/p>\u003Cp>I tell you, it did it does work. You just gotta have the the right movement.\u003C/p>\u003Cp>Speaker 1: Andrew, I full on believe you. You you don't have you're not proving anything. Don't worry.\u003C/p>\u003Cp>Speaker 0: Come on. Really?\u003C/p>\u003Cp>Speaker 1: Your your taste.\u003C/p>\u003Cp>Speaker 0: Oh, did we see that? We did it. We did it. So here's the thing. Right?\u003C/p>\u003Cp>So we changed it from right to up. So we're going right. We're going right. We're going right. And then suddenly, we start going up.\u003C/p>\u003Cp>And that's there's no logic in here that says, dearest, dearest ready set battlesnake, avoid that wall. This is all just, Andrew changing that logic in real time. And you can see if we go to the logs there, which are gonna be, like, super overrun now, not that logs, these logs. But you can see here in our terminal there we go. Yeah.\u003C/p>\u003Cp>So we're updating our cached app, updated cache direction app. And so you can see that there's, like, the change that happens in here. Yeah. Oh, and this is the interesting thing too here. Right?\u003C/p>\u003Cp>Is you actually see so most of the time, it's updating cash Kevin, are you impressed? Are you blown away by my amazing implementation of this?\u003C/p>\u003Cp>Speaker 1: I am so blown away. Thank you so much. I feel like my life has been genuinely enriched in the last 39 minutes of recording. Thank you, Andrew. I don't\u003C/p>\u003Cp>Speaker 0: know what I would do without you as\u003C/p>\u003Cp>Speaker 1: a friend, a mentor, a colleague.\u003C/p>\u003Cp>Speaker 0: Thank you. Thank you. Good. Yeah. And that's it.\u003C/p>\u003Cp>Episode over. We're done. Episode wrapped. Thanks. So, that was just like this is I feel like in every good episode, in every good coding experience, you should do something that's totally useless and makes absolutely no sense so you have the knowledge and skills to do something that actually makes sense.\u003C/p>\u003Cp>Speaker 1: But there are some things already set up. Right? So we've got the dev cycle, like, prod forgive my terminology if I use the wrong terminology. But we've got the project set up, we've got the variable, we've got the options, we've got the ability to change it and hit save. We have a client, you know, we have like an ID for this specific feature.\u003C/p>\u003Cp>So, you know, we have all the parts, and we can just apply it in\u003C/p>\u003Cp>Speaker 0: Exactly. Different ways. So what we're gonna need to do here is I'm going to so we're gonna forget that all of this ever happened. We're gonna go shut off ngrok, for a second, and then we are going to go back to our direct us instance, and we're gonna start direct us up again so we can start to have some fun. And we've got all of our code here.\u003C/p>\u003Cp>Why\u003C/p>\u003Cp>Speaker 1: We do. Are you It's it's it's the other it's the other Visual Studio code that's in the back. Yeah. Are you\u003C/p>\u003Cp>Speaker 0: talking about? What? Hold on. Alright. We'll doc, and then we'll go back over here to engrok, and let's get this running.\u003C/p>\u003Cp>Oh, I'm running the wrong one. There we go. Nice. Wonderful. And let's go over to here, which I should actually have open in my window.\u003C/p>\u003Cp>Oh, and we don't need this for right now. So let's open that and make that bigger. And we should be able to go to here. Wonderful. I am gonna need to log in again, I think.\u003C/p>\u003Cp>Speaker 1: No. No. No. I think it'll be fine.\u003C/p>\u003Cp>Speaker 0: No. I need to log in again.\u003C/p>\u003Cp>Speaker 1: Oh, you're gonna need to log in again. Battlesnake@lws.iohunter2. Glasses us what I set this up with. There you go.\u003C/p>\u003Cp>Speaker 0: Okay. So here is our logic. We removed a bunch of stuff from in there. Great. Our snake is working again.\u003C/p>\u003Cp>Should be working again. Logs are all there. I feel less bad about all the logging that I was doing of every time that we were calling Dev cycle now that I see these Exactly.\u003C/p>\u003Cp>Speaker 1: Thousands of We actually you know, this was I mean, all the dates, like, visible elsewhere. Just today, we actually recorded, or we we did a live request review, which is our show where we or it's our live event, which then becomes a show on director's TV a week later, where we go through feature requests to talk about them, and today's was all about flows improvements. And one of the big things is the ability to change the retention of those logs. I like that. Because, yes, they get they get chunky.\u003C/p>\u003Cp>Speaker 0: They do. So, what I think we are going to do, and I liked the idea that you were talking about before, Kevin. I think let's do 2 pieces of logic, and we're gonna we're not gonna delete all of this stuff. We're just gonna kinda remove it for right now, and then we'll reconnect it. And I think we'll have just right now 2 different blocks.\u003C/p>\u003Cp>1 of them that returns right, one of them that returns left for moves, and we'll have some branching logic in there. How does that sound to you?\u003C/p>\u003Cp>Speaker 1: Let's create a new flow for this, and then we could just hook it up to the extension, rebuild it. Like, I think that might be a good a good shout.\u003C/p>\u003Cp>Speaker 0: Let's do that. So let's call this snake, cycle. Alright. And Great. We're just gonna leave that.\u003C/p>\u003Cp>Speaker 1: All of that's good. Just next.\u003C/p>\u003Cp>Speaker 0: And webhook or webhook. Yeah. Okay. And then get, I think.\u003C/p>\u003Cp>Speaker 1: Host.\u003C/p>\u003Cp>Speaker 0: Not the get. Not I had a 5050 shot there, and I got it wrong. There we go. No async\u003C/p>\u003Cp>Speaker 1: calls either. Because because we'll get because we'll get a post request from the Battlesnake platform. Yeah, data of last operation safe. And then what we wanna do here is grab that URL. Yeah.\u003C/p>\u003Cp>I think yeah. Perfect. Go go to the code editor. Go to the extension called snake. Yeah.\u003C/p>\u003Cp>Speaker 0: I think so. And then in our source index dot JS, and we just wanna update this. Right?\u003C/p>\u003Cp>Speaker 1: For move for move. The move one. And just to not have to do the round trip, we'll change the for local host 8055. But hit paste. That'll give us the ID\u003C/p>\u003Cp>Speaker 0: of the trigger and everything.\u003C/p>\u003Cp>Speaker 1: Yep. And then, yeah, we'll just change that for HTTP, not HTTPS, localhost\u003C/p>\u003Cp>Speaker 0: secure my how to secure my URLs. Kevin, who who are won't work. Phone's bloody work, man. Actually, it does. No.\u003C/p>\u003Cp>No. It does. I'm pretty sure. I'm pretty sure ngrok automatically anyways, I don't I don't need to\u003C/p>\u003Cp>Speaker 1: Not not on not on this one. Yeah. Yeah. But yeah. Yeah.\u003C/p>\u003Cp>Yeah. But not on the local not on local host. If you were doing it via ngrok, but then we're gonna have to do another round trip to ngrok for what? It runs locally.\u003C/p>\u003Cp>Speaker 0: Smart. Okay. So we've got our trigger there. This is our move. So now this is all feeding through to the place that\u003C/p>\u003Cp>Speaker 1: we want. Now we've got to rebuild the extension and restart directives.\u003C/p>\u003Cp>Speaker 0: Npm run build?\u003C/p>\u003Cp>Speaker 1: Yeah. From the from that extensions directory. So let's create a new terminal in here.\u003C/p>\u003Cp>Speaker 0: Yeah. CD extensions.\u003C/p>\u003Cp>Speaker 1: Yeah. You got it.\u003C/p>\u003Cp>Speaker 0: And CD directives Extension.\u003C/p>\u003Cp>Speaker 1: Extension snake. Jake and m You got it. Build. You got it.\u003C/p>\u003Cp>Speaker 0: And then restart And then we just Oh, because cannot.\u003C/p>\u003Cp>Speaker 1: Okay. This is fine. This is like macOS going overboard. Open up your open up your settings on your Mac. Security, I think it is.\u003C/p>\u003Cp>Speaker 0: Yeah. We may not be able to do that just because of the security settings. Security\u003C/p>\u003Cp>Speaker 1: No. It was there. It said privacy and security.\u003C/p>\u003Cp>Speaker 0: Security settings.\u003C/p>\u003Cp>Speaker 1: No. Just yeah. There you go. Allow anyway. It's right there.\u003C/p>\u003Cp>It's right there. Fsevents.net. Yeah. Yeah. Cool.\u003C/p>\u003Cp>Yeah.\u003C/p>\u003Cp>Speaker 0: Great. Alright. Let's try this again.\u003C/p>\u003Cp>Speaker 1: You got it.\u003C/p>\u003Cp>Speaker 0: Beautiful. Okay.\u003C/p>\u003Cp>Speaker 1: Lovely. And then restart and then restart the Docker container. Fixed in directory version 10.9, which we have not upgraded to right now. There you go. Hooray.\u003C/p>\u003Cp>And now you're good.\u003C/p>\u003Cp>Speaker 0: Okay. So let's head back over to Directus, which is Yeah. Somewhere. It exists. It is in the world on this device.\u003C/p>\u003Cp>Too many windows. Directus. There we go. Okay. So here we go.\u003C/p>\u003Cp>So this should be talking here. I'm actually gonna go start a game just to make sure this is working. So let's go, play.battlesync.com. Let's go create game. And I think yeah.\u003C/p>\u003Cp>Does it still save recently used? It doesn't. That's so sad. Used to do recently saved escape. There we go.\u003C/p>\u003Cp>And if this is working, we should see a log showing up here. Don't look. Happen. Just no. That's fine.\u003C/p>\u003Cp>We don't it's probably not gonna work work, but it should show a log here. We have 10 logs. I love it.\u003C/p>\u003Cp>Speaker 1: Awesome.\u003C/p>\u003Cp>Speaker 0: Yeah. Okay. Method post. Yes. So that's 10 moves that it took to pass away.\u003C/p>\u003Cp>Perfect. Logic is coming through there. Accountability. Awesome. Okay.\u003C/p>\u003Cp>It's working. So now we're gonna do some branching logic in here. So we're gonna go and condition. And we're gonna worry about the condition afterwards, but let's call this dev cycle response.\u003C/p>\u003Cp>Speaker 1: Oh, it's\u003C/p>\u003Cp>Speaker 0: not here.\u003C/p>\u003Cp>Speaker 1: Well, hang on. Not in not in a condition. 1st, we need to go get data and then we run conditions on the results. So I'm assuming we can just make I'm assuming with DevCycle, just an API, we can make an API request, right, just a raw API request. So you want the webhook request URL, one there.\u003C/p>\u003Cp>Perfect. And first, we need to go get the data, then we'll do the conditional logic. So this might be a moment to look at the dev cycle docs.\u003C/p>\u003Cp>Speaker 0: Yeah, that would make sense. We should probably Yeah.\u003C/p>\u003Cp>Speaker 1: And there will be a moment here where we'll we'll redact it for for the recording, but you're gonna have to authenticate yourself. So you're probably gonna need to put in, like, an API key or token. Can you create multiple inside of your dev cycle?\u003C/p>\u003Cp>Speaker 0: That's a good question. We're gonna experience this together. Yeah. I haven't played around with the yeah. So we're gonna do a little bit of this together, and then we're gonna not do a little bit of this together.\u003C/p>\u003Cp>So let's head over there. So jumping into the dev cycle docs, APIs, management API. This is to create features and variables. I don't think it returns anything. We actually want the bucketing API.\u003C/p>\u003Cp>So this is used to fetch on HTTP. Yes. Yes. Yes. Unique identify the user.\u003C/p>\u003Cp>Definitely, this bucketing API. Get all the variables. Get all the features. Post events. Get variable by key.\u003C/p>\u003Cp>Okay. This is what we're looking for. So let's head over to bucketing API, and we are going to have to get our key Wait. What what my key?\u003C/p>\u003Cp>Speaker 1: Yeah. Sorry. What's the endpoint we wanna hit?\u003C/p>\u003Cp>Speaker 0: So we are going to do get variable by key for user data, I think. Let me look here. Private platform SDK type. Missing SDK. Get all variables.\u003C/p>\u003Cp>What is that gonna return? Is that gonna return the status?\u003C/p>\u003Cp>Speaker 1: What's our what's our end goal here? Our end goal is to\u003C/p>\u003Cp>Speaker 0: We wanna just get the value for the variable. So it's very similar to what we're dealing with in our SDKs.\u003C/p>\u003Cp>Speaker 1: To the user ID to start.\u003C/p>\u003Cp>Speaker 0: Yeah. But everything is hidden behind our bearer token. So I'm just gonna find where all of this is hidden. So key type variation, variation in variation key, response examples, get all features by key post events. No.\u003C/p>\u003Cp>Fetch\u003C/p>\u003Cp>Speaker 1: No. I think get all fee I think I think you were right. Get all features by key for user or get all variables by key for user data. And this will no. Because this will\u003C/p>\u003Cp>Speaker 0: return Here it is. Yeah. Here it is. The value. Fantastic.\u003C/p>\u003Cp>That's what we're that's what we're after is the value. So, we're gonna go offline for one second as I go and get all of my authentication stuff sorted out, and, then we will be back with our API stuff.\u003C/p>\u003Cp>Speaker 1: Sounds good. Okay. So For what for what it for that's worth, we could totally have kept it going, and we could just redact it, adjust that slither slither\u003C/p>\u003Cp>Speaker 0: and time. Keep it going. Yeah. Let's do that. Alright.\u003C/p>\u003Cp>We're back. Yeah. We haven't gone anywhere. We thought we were gonna go somewhere. This is interesting too because we if an so, technically, our management and bucketing API are actually using the same stuff.\u003C/p>\u003Cp>I need to update our docs because we should actually have this curl command in, both, and they're not both there. I guess most people are just assuming we're gonna be using that. So, I'm gonna go and, in a different window, actually, open up, my OAuth token using the client ID. Okay. So I need to go over and what do I need here?\u003C/p>\u003Cp>Client ID and client secret in my dashboard. Alright. So let's go over here and yeah. I don't think\u003C/p>\u003Cp>Speaker 1: Yeah.\u003C/p>\u003Cp>Speaker 0: Showed anything.\u003C/p>\u003Cp>Speaker 1: I'm a I'm a little bit fuzzy on how you authenticate.\u003C/p>\u003Cp>Speaker 0: Let me show you. Okay. So what we do here is alright. You're seeing my screen now. Hey?\u003C/p>\u003Cp>Speaker 1: Yeah.\u003C/p>\u003Cp>Speaker 0: So and, again, I will fully admit that I have only messed around with our API in a very limited way. So, basically, we are doing a curl request with our client ID and client secret. We can actually obviously do this same post request in Directus, and we're just using our client ID and client c. We can't do it directly and directly.\u003C/p>\u003Cp>Speaker 1: I've just spotted it. The type the encoding type for this, for this request is a URL encoded form, but that request URL, you know, operation that's built in only sends JSON. And they're just you don't work around this. Like, you can't do it. However, I'm not too too sad because you can, of course, do it using a custom endpoint.\u003C/p>\u003Cp>It's not quite what we wanna do, but could we do or building a little custom operation. So maybe that's what we do in order to to do that round trip. It's not the ideal, like you know, it means we're gonna have to spend some time doing a little bit of extension development. Like, just spotted that. Is that is that the only way to do it?\u003C/p>\u003Cp>Speaker 0: Yep. Yeah. We have no real public API stuff, which is interesting, but also so what's interesting so we are having so we're working right now on some webhook stuff. So you can technically get when, certain changes have been made, like an audit log. You can read that through a webhook, but you can't actually read what people's values are.\u003C/p>\u003Cp>Speaker 1: Because that would that would be so good because on a webhook, you could trigger a separate flow, store indirect just like your cache Yeah. And then do stuff based on that. But that's completely fine. So we could do this or if we're building a a little custom, operation, we could just straight up use the SDK.\u003C/p>\u003Cp>Speaker 0: Yeah. So let's do that. And then we've already got the code there. So, yeah. Okay.\u003C/p>\u003Cp>Let's, are we gonna do this here inside of our code, or are we gonna do this over on the platform to get started? Where are we gonna\u003C/p>\u003Cp>Speaker 1: I don't under let let's let's build an operation for flows as an extension. And the goal of that operation is to, right now, just get a fixed, you know, a fixed response from from the API. So, no, we don't do it like this. We actually use the extensions SDK CLI, which will boil the plate a whole extension for us.\u003C/p>\u003Cp>Speaker 0: Alright. We're gonna delete that bad boy. Alright. Let's open\u003C/p>\u003Cp>Speaker 1: a new terminal here. Open up a new terminal. You got it. And we'll go into the extensions folder.\u003C/p>\u003Cp>Speaker 0: Alright. Oh, into the extensions folder. Extensions folder. Yeah. Yeah.\u003C/p>\u003Cp>Speaker 1: Yeah. Sorry. Yeah. Yeah. Exactly.\u003C/p>\u003Cp>Exactly.\u003C/p>\u003Cp>Speaker 0: And then how do I pull up the docs? Or are you just gonna Yeah. Docs. I mean,\u003C/p>\u003Cp>Speaker 1: I could tell I could tell you this one, but let let's let's do the docs. Let's let's have the authentic experience with someone who doesn't live in this every day.\u003C/p>\u003Cp>Speaker 0: There we go. Alright. Let's head over. And we're gonna see this is gonna be my challenge. How long does it take Andrew to find out what I'm looking for?\u003C/p>\u003Cp>Alright. So when direct to stocks, we're in extensions. We're in device extensions.\u003C/p>\u003Cp>Speaker 1: Zoom in.\u003C/p>\u003Cp>Speaker 0: We're going to zoom in. We're going to go extensions. We're going to go, developing extensions and we're gonna go extension services and introduction. No. That doesn't look like what I'm looking for.\u003C/p>\u003Cp>Speaker 1: Maybe Very good. Very very good little piece of feedback there. Go into the fundamental section, the one you haven't opened out of the 3, the last one, and creating extensions.\u003C/p>\u003Cp>Speaker 0: Perfect. We're just gonna use this. Alright. Let's go back over here and run this.\u003C/p>\u003Cp>Speaker 1: But I I do see that. I do see that path you took. I'm glad to know that. Discussed it with one of our colleagues just yesterday. So yeah.\u003C/p>\u003Cp>Speaker 0: Like, when things work oh, oh, I need to change my note version. It's upset with me. Wait. What? Oh, it's just warnings.\u003C/p>\u003Cp>Really? Oh, yeah. Whatever. Sure.\u003C/p>\u003Cp>Speaker 1: Me. That looks\u003C/p>\u003Cp>Speaker 0: what do\u003C/p>\u003Cp>Speaker 1: you wanna create? Operation, please.\u003C/p>\u003Cp>Speaker 0: Oh, there it is. Hidden down in that list. Choose a name.\u003C/p>\u003Cp>Speaker 1: And they so they must start so they must start as of this release. I think it might be gone in 10.9 or it'll be gone in a soon release in a upcoming release. Directus dash extension dash whatever you want. Dev cycle is fine.\u003C/p>\u003Cp>Speaker 0: JavaScript because I don't play with TypeScript.\u003C/p>\u003Cp>Speaker 1: I respect that.\u003C/p>\u003Cp>Speaker 0: Dependencies. Yes. Scaffolding it. I love this. This is so smooth.\u003C/p>\u003Cp>I like a I like a good CLI.\u003C/p>\u003Cp>Speaker 1: Good. Great. So now we have if you go into the extensions drop down, you have a new extension called dev cycle inside of source. We have API dot JS. We have App JS.\u003C/p>\u003Cp>App JS is for all of the configuration. So it's all the UI part. It's all of the options when you create a new one, you have that little pane that pops out. It's the information that will show on the on the card in the overview where you connect to all the nodes. That's the overview there.\u003C/p>\u003Cp>And then the API dot JS is the node code that will actually be executed in the background. Right?\u003C/p>\u003Cp>Speaker 0: So we're gonna go and I think right now what we wanna do is actually grab, just so we're not having to worry about this later. We're gonna grab our code from here. So not going back and forth, because this is basically all the stuff that I need.\u003C/p>\u003Cp>Speaker 1: Yeah. Great.\u003C/p>\u003Cp>Speaker 0: And we're just gonna put you down there and comment you out so that things don't get upset with us.\u003C/p>\u003Cp>Speaker 1: Sounds good.\u003C/p>\u003Cp>Speaker 0: Alright. Perfect. So let's save that, and let's do an install. So I do have to have to go into this folder to install, slash extensions. Directus extension CD.\u003C/p>\u003Cp>Extension. CD. Cycle. Not we're not we're gonna put it at the end and see what happens. No.\u003C/p>\u003Cp>Start. Start. Start. Start.\u003C/p>\u003Cp>Speaker 1: For a moment, I was like, am I about to learn something?\u003C/p>\u003Cp>Speaker 0: No. There's there's very little learning that will be\u003C/p>\u003Cp>Speaker 1: There's very little learning. I got it. I got it.\u003C/p>\u003Cp>Speaker 0: And then we gotta go over to because we're gonna install the, the SDK. We're gonna go over to the dev cycle docs. So we'll move back over to docs, and let's head back over to the dev cycle docs, docs.devcycle.com. And we're gonna head over to our SDK section, our node JS SDK\u003C/p>\u003Cp>Speaker 1: You got it.\u003C/p>\u003Cp>Speaker 0: Installation. And let's grab this because who uses yarn, NPM or bust? I've become so opinionated in my coding Alright. So that means that our import statement should work now. Actually, is this import or is this require?\u003C/p>\u003Cp>I This is import. Yeah. I thought so. Alright. Let's move you to the top.\u003C/p>\u003Cp>Alright. Perfect. So step 1 complete. We're done. Time to go home, everybody.\u003C/p>\u003Cp>End of episode.\u003C/p>\u003Cp>Speaker 1: So a couple of things that are just important before we continue. There is an ID here called custom. Let's rename that, and then we have to reflect that ID in the App JS and the corresponding App JS. I think dev cycle is a perfect ID. So we just want those 2 to match, first of all.\u003C/p>\u003Cp>Very good. And let's, while we're here, give this a name. Dev cycle, this is the nice friendly name that shows up when you pick your operation type. Sure. Yeah.\u003C/p>\u003Cp>Then are we gonna ask the user for any configuration up front? And I'm gonna say that maybe we do. Maybe we wanna collect the snake eye like, the the the ID. Right? Are we gonna do it?\u003C/p>\u003Cp>Are we gonna have time to do that? I mean, can we give it just an arbitrary ID, or does do you have to register users inside of the dev cycle system first? Like, how does that work?\u003C/p>\u003Cp>Speaker 0: So you would need to feed a specific user's information. Yeah. Let's call a snake name. Yes. I think that's a great idea.\u003C/p>\u003Cp>Yeah.\u003C/p>\u003Cp>Speaker 1: I was a snake ID. Snake ID. So let's do that. And at first, we'll just hard code it, but later, you can put dynamic values in here in flows. So instead of text, let's just change text to snake.\u003C/p>\u003Cp>So there's a friendly name. Yeah. Yeah. Perfect. There's a friendly name.\u003C/p>\u003Cp>Speaker 0: Yes. Is that correct? Is it? I changed this from tests from is this yeah?\u003C/p>\u003Cp>Speaker 1: Yes. That's all good. The options need to change too. That's the part. So that that's the overview that will show on the card.\u003C/p>\u003Cp>Cool. Whatever. It's the options we care about. That's the pain that comes out and you select all the options. So in here, the field, the field will be called snake.\u003C/p>\u003Cp>And that's what corresponds with the snake in the overview. That's the friendly name. It will still be a string. It'll be full of input. Great.\u003C/p>\u003Cp>Save. Now we'll go over to API dot JS, and there's text here. We'll change text for snake. So that says taking in the snake value and now we're going to execute some code. But before we do this, I think that's everything on the UI side done.\u003C/p>\u003Cp>We want to initialize this client and we'll want to initialize this client between lines 13. So everything in your yeah.\u003C/p>\u003Cp>Speaker 0: Great. So, we're going to ignore this because we're not actually going to I'm actually I'm interested to see how this works. Once we have done this, we're going to change some things up eventually. But we're not gonna worry about that right now. So let's grab\u003C/p>\u003Cp>Speaker 1: Oh, which was straight upon commenting everything. That's that's crazy.\u003C/p>\u003Cp>Speaker 0: I mean, nothing could go wrong because nothing's really running yet. So this is good. This server is gonna change. I'm gonna leave these. I don't actually know if we need these, but I'm not gonna question it.\u003C/p>\u003Cp>Speaker 1: If it works before, our goal is to take what worked and apply it here. So we're not gonna make any changes to the logic here.\u003C/p>\u003Cp>Speaker 0: Yeah. Exactly. So we are initialized there now, and now we need to pull all of this stuff up above. And I think we're not gonna need that get cached direction elsewhere.\u003C/p>\u003Cp>Speaker 1: I don't think we wanna necessarily play with that right now. And at the very least, we wanna make sure that we can get data from dev cycle in a flow Exactly. And then pass it on. But if you were having an issue where it's taking a little while to do that round trip, there's a pretty good chance that we're gonna hit that here too.\u003C/p>\u003Cp>Speaker 0: Okay. So let's go here. Awesome. Alright. So here we go, dev cycle clients.\u003C/p>\u003Cp>So we're established here. We've got this cash direction. Again, I don't actually think we need this anymore because we're gonna be calling it sort of once to determine what's gonna change. We're not gonna be calling this again and again and again. Right?\u003C/p>\u003Cp>We're not gonna be calling this every move. Is that our that's our focus. Right?\u003C/p>\u003Cp>Speaker 1: I mean, we were. Yeah.\u003C/p>\u003Cp>Speaker 0: Do we we were gonna call it every move? Oh,\u003C/p>\u003Cp>Speaker 1: this is all in the wrong endpoint. This actually wants to go in the start game, doesn't it? Once you're in the start game and then for the game, we determine the strategy and then we execute on it. This is okay. Let's keep rolling now.\u003C/p>\u003Cp>And then before we've done it with yeah, exactly. Precisely. So let's really let's let's keep this light. In fact, I think doing everything in a separate function may not even be what we Exactly. Want too much here.\u003C/p>\u003Cp>Speaker 0: So I'm gonna pull all of this out of this update cache directory.\u003C/p>\u003Cp>Speaker 1: Hang on. Hang on. I think the only bit we care about is this dev cycle client variable.\u003C/p>\u003Cp>Speaker 0: I think\u003C/p>\u003Cp>Speaker 1: that's the only bit we want.\u003C/p>\u003Cp>Speaker 0: We also care about this user thing. So that's\u003C/p>\u003Cp>Speaker 1: where that's going to snake. Yeah. But that's going to that's already that's coming in from the snake value above. So we're gonna get to feed that in.\u003C/p>\u003Cp>Speaker 0: Yes. Yes. Yep. I guess it has to feed a very specific way. It has to be a very specific object that's feeding in, but maybe you're right.\u003C/p>\u003Cp>Let's let's see. Let's see. I think I think\u003C/p>\u003Cp>Speaker 1: it'll be okay. So so if we if we go down to where the actual flow handler is, which I think is down. So inside of the handler, instead of the console log, snake is a string. So what we want to do here is we want to make on line 41, we want to make this an async function. Right?\u003C/p>\u003Cp>Yep. No. As in as in we wanna change just before the the snake. Oh, I see. We wanna make it async, then we'll just use a sync yeah.\u003C/p>\u003Cp>One more to the left. One more to the left.\u003C/p>\u003Cp>Speaker 0: Goodness gracious. And we're gonna get rid of you. You don't need to be there.\u003C/p>\u003Cp>Speaker 1: And then and then inside, what we wanna do is is run that run that SDK method, but we can do it with a wait instead of then just to clean it up in this context. So if you scroll just very slightly up oh, yeah. We'll ditch something small. Yeah.\u003C/p>\u003Cp>Speaker 0: We're just gonna do this. We're not gonna need to do this call. Perfect. Let's just work the day.\u003C/p>\u003Cp>Speaker 1: Grabbing this. Yeah. But only 17 18 to start.\u003C/p>\u003Cp>Speaker 0: Oh, yeah. You're right. Yep.\u003C/p>\u003Cp>Speaker 1: Because we're doing a sync await.\u003C/p>\u003Cp>Speaker 0: Yep. Yep. Absolutely.\u003C/p>\u003Cp>Speaker 1: So we want that. That goes straight in there, within within a wait at the beginning of line 33 and a store this in a variable. Right?\u003C/p>\u003Cp>Speaker 0: Yeah. Let's call this strategy.\u003C/p>\u003Cp>Speaker 1: Beautiful. Love that. And then user is now gonna be I mean, if you wanna grab lines 13 to 15 just to get the structure, but really it's just an object with the user ID and snake. Yeah. Yeah.\u003C/p>\u003Cp>Exactly.\u003C/p>\u003Cp>Speaker 0: Let's see what happens. And then yeah. So we're gonna Other way. We're gonna define this above. Yep.\u003C/p>\u003Cp>We can assign this above. Awesome. And we're actually gonna feed this in. User ID here is gonna be snake. So we're gonna feed in snake here.\u003C/p>\u003Cp>Speaker 1: Snake? Yeah.\u003C/p>\u003Cp>Speaker 0: I think that should work.\u003C/p>\u003Cp>Speaker 1: I think that should work. But we don't know what comes back in strategy yet, so we're gonna log it. Perfect. And then we're going to return strategy.\u003C/p>\u003Cp>Speaker 0: Beautiful. And that's actually add some comments there so we know it's being logged. Strategy.\u003C/p>\u003Cp>Speaker 1: Oh, can I show you can I show you a quick tip for this, which I think you'll\u003C/p>\u003Cp>Speaker 0: really like? Love a quick tip.\u003C/p>\u003Cp>Speaker 1: So remove that remove that string in the comma from the console log. So it's just the word strategy again. Yep. And then highlight all of the word strategy, just the word strategy, and then surround them with squiggly brackets. And now that is an object where the key is strategy and the value is strategy.\u003C/p>\u003Cp>So when it logs, you get strategy, colon. Yeah. It will be an object. So that is just a real shorthand if you if you just want to quickly tell what something is and you have just a variable key.\u003C/p>\u003Cp>Speaker 0: You know, sometimes I enjoy JavaScript, but only when people that enjoy JavaScript are doing JavaScript with me. Okay. So technically, this should this should do all of the things that we want it to do, I think.\u003C/p>\u003Cp>Speaker 1: Yeah. And\u003C/p>\u003Cp>Speaker 0: so this is wanna change this to hold on. Oh, sorry. Go ahead, Kevin.\u003C/p>\u003Cp>Speaker 1: No. No. No. Go ahead.\u003C/p>\u003Cp>Speaker 0: So I'm just saying here so here, we're actually gonna go and create an so this is a direction variable. We're gonna create a strategy variable and kind of put 2 strategies. We're gonna do, like, hungry and tailchase or something like that.\u003C/p>\u003Cp>Speaker 1: I mean, we we're not gonna implement those. So why don't we do left or\u003C/p>\u003Cp>Speaker 0: right? That is a very smart decision, Kevin. So let's head back over to dev cycle here, and we're gonna get out of there, go into our features again. Into our choose move, it doesn't really matter again what this feature is called. We're gonna create a new variable.\u003C/p>\u003Cp>We're gonna call this strategy. We're gonna set this as a strict technically, we could just do Boolean, but we'll do strategy. And because of the way that we have this set up with, like, these 4 different options of left, right, up, down, we're gonna actually go ahead and kind of move this around. So we're gonna call it, this one's gonna be left, and this one's gonna be right. Actually, we'll just leave it all exactly the same.\u003C/p>\u003Cp>Down, up.\u003C/p>\u003Cp>Speaker 1: But it's just called strategy this time.\u003C/p>\u003Cp>Speaker 0: It's called strategy now. Yeah. Sure. Alright. Amazing.\u003C/p>\u003Cp>Look at that. We changed literally nothing. I love that I did a thing that was completely unnecessary. We could\u003C/p>\u003Cp>Speaker 1: have just kept the stream.\u003C/p>\u003Cp>Speaker 0: Just kept the same.\u003C/p>\u003Cp>Speaker 1: That's fine. That's fine. We we okay. So we're logging this and we I think I think this works, but what we want to do here is is\u003C/p>\u003Cp>Speaker 0: what do we\u003C/p>\u003Cp>Speaker 1: wanna do here? We now wanna hook this up to a start.\u003C/p>\u003Cp>Speaker 0: Yes. So we need to do we need to do a new end.\u003C/p>\u003Cp>Speaker 1: And we need to change the data model to, have this extra, like, strategy string, which I think is amazing because then even in our insights dashboard, we can be like, when using this strategy, this is the win rate. Like, it's actually really lovely.\u003C/p>\u003Cp>Speaker 0: Now we're done in code for right now, though. Right? So I think that we can No. We're not?\u003C/p>\u003Cp>Speaker 1: Build this one. Build this extension. I think we're gonna need to change which flow is being hit on start. I think we need to create a new flow. We need to create a hang on.\u003C/p>\u003Cp>Speaker 0: Oh, metadata.\u003C/p>\u003Cp>Speaker 1: Hang on. Hang on. Is this are these errors or are these suggestions? I think this is all about and has been rewritten. I have a sneaking suspicion this is inside of the SDK.\u003C/p>\u003Cp>There\u003C/p>\u003Cp>Speaker 0: might be no module in\u003C/p>\u003Cp>Speaker 1: But it said done.\u003C/p>\u003Cp>Speaker 0: Yeah. This may\u003C/p>\u003Cp>Speaker 1: And it says and has been rewritten. All of these have resolutions. I've not seen the error before. I have a sneaking suspicion it's in the dev cycle and the way we're rolling up and bundling code, not a problem. If it works, it works.\u003C/p>\u003Cp>Not sure if it works. We'll figure that out. So what we actually wanna do is create now another\u003C/p>\u003Cp>Speaker 0: Flow. Flow. Do we need to create another item? No.\u003C/p>\u003Cp>Speaker 1: No. We don't. We can alter our existing start endpoint. Yeah. So yeah.\u003C/p>\u003Cp>But you've built this. You need to restart Directus.\u003C/p>\u003Cp>Speaker 0: Yeah. Okay. There we go. We're gonna restart Directus. Oh, I need\u003C/p>\u003Cp>Speaker 1: there we\u003C/p>\u003Cp>Speaker 0: go. Let's restart here. Oh, wrong one. Just ignore the fact that we're rebuilding it again. Ignore all of those errors, everybody, and go to the right ones.\u003C/p>\u003Cp>Speaker 1: To me to me, they read as warnings. That's the spirit.\u003C/p>\u003Cp>Speaker 0: Alright. And that should be running again, and we're gonna close that down. We're gonna get the right thing open now. And I think we were here. Let's mark\u003C/p>\u003Cp>Speaker 1: this out. Absolute chaos. I literally can't get behind your window management.\u003C/p>\u003Cp>Speaker 0: There is no there is no window management that is occurring. There is nothing. So,\u003C/p>\u003Cp>Speaker 1: there is one other thing we want to do just before we hook all this up, which is we want to go into the data model. And for a gain, we want to, just add a string strategy.\u003C/p>\u003Cp>Speaker 0: Right?\u003C/p>\u003Cp>Speaker 1: Yep. Yeah. Lovely. And then just because it makes can you make it half width, please? Sure.\u003C/p>\u003Cp>You use you you could do it with the little 3 dot things on the side come out of this pane, the little 3 dot thing, and then half Got it.\u003C/p>\u003Cp>Speaker 0: Got it. We got there. Alright. So that's in there now. Awesome.\u003C/p>\u003Cp>Let's head back into flows. Oh, that's not the right spot. Back into flows, and we're gonna change this one. I wanna change the name here before I forget. Can I change that?\u003C/p>\u003Cp>Yeah. I can.\u003C/p>\u003Cp>Speaker 1: Yeah. Yeah. Back here. Yeah. Yeah.\u003C/p>\u003Cp>Yeah.\u003C/p>\u003Cp>Speaker 0: Sneak strategy. Awesome. Nice. Perfect. And\u003C/p>\u003Cp>Speaker 1: then, yes, start. And now we want to see if this new thing can so we extract the snake count, and then we create data. And we basically want to create one there in the middle, which hopefully won't pose too many issues for us.\u003C/p>\u003Cp>Speaker 0: Let's build\u003C/p>\u003Cp>Speaker 1: So no. No.\u003C/p>\u003Cp>Speaker 0: Not on\u003C/p>\u003Cp>Speaker 1: the on the success path.\u003C/p>\u003Cp>Speaker 0: Great. And what do we do in our custom operation? Right?\u003C/p>\u003Cp>Speaker 1: Yes. There is at the bottom.\u003C/p>\u003Cp>Speaker 0: Look at that. It even says dev cycle. I think that's a good name to keep for it. I like that. I also like that it converts camel case into, what's what's this?\u003C/p>\u003Cp>What's the\u003C/p>\u003Cp>Speaker 1: that is called I wanna say hang on. I will I will tell you this because I actually have it inside of my raycast case. That is called that is called, coming up, camel case.\u003C/p>\u003Cp>Speaker 0: That's camel case? This is camel case.\u003C/p>\u003Cp>Speaker 1: Oh, no. Sorry. Sorry. That is I'm I'm I've picked a ridiculous string to test against. So it had, like, it had, like, hang on.\u003C/p>\u003Cp>That is called, scrolling through, Pascal. Is it snake or is it Pascal case? Snake case. It's called snake case.\u003C/p>\u003Cp>Speaker 0: What is snake? Enough. That's a case. There we go. That is\u003C/p>\u003Cp>Speaker 1: appropriate.\u003C/p>\u003Cp>Speaker 0: It's almost like I knew this, and I wanted to bring it up. Okay. And for a sneak ID, we can actually put in anything. We're just\u003C/p>\u003Cp>Speaker 1: Oh, encoded.\u003C/p>\u003Cp>Speaker 0: Yeah. We're just gonna put, like, a really basic actually, no. Let's call this ready, set, Battlesnake because there's some cool stuff you can do with your targeting rules, including include. So let's just set that.\u003C/p>\u003Cp>Speaker 1: I mean, remember, that can be a dynamic value from the first step.\u003C/p>\u003Cp>Speaker 0: It can. But for now, we'll But\u003C/p>\u003Cp>Speaker 1: we can we can yeah. Absolutely agree. So inside of the dev cycle step now sorry. Can we just go in? That was the only option.\u003C/p>\u003Cp>And so dev cycle is gonna return some data. Yeah. All of that happens in the background. So we don't know whether it works yet. So now we wanna redo the nodules.\u003C/p>\u003Cp>Speaker 0: Yeah. Oh, yeah. We just wanna move this to here. Right?\u003C/p>\u003Cp>Speaker 1: Yeah. And this is where we seem to keep having a bad time.\u003C/p>\u003Cp>Speaker 0: Awesome. Thank you, Directus, for doing exactly what we were expecting you to do.\u003C/p>\u003Cp>Speaker 1: On behalf of the core team, you are very welcome.\u003C/p>\u003Cp>Speaker 0: So what is our issue in here? What is our issue?\u003C/p>\u003Cp>Speaker 1: But remember, we had this all the way from the first episode, and we just kind of wrote like, there was a specific set of moves we did, and it worked.\u003C/p>\u003Cp>Speaker 0: Okay. That works. I think it\u003C/p>\u003Cp>Speaker 1: might have been this. I hit edit again.\u003C/p>\u003Cp>Speaker 0: Yeah. And then bring it back and then bring it back. No. What if I just disconnect that altogether? K.\u003C/p>\u003Cp>That works.\u003C/p>\u003Cp>Speaker 1: We're getting there. And then and then do it again now? Edit and just do that last one?\u003C/p>\u003Cp>Speaker 0: Come on. There we go. There we go. We got it. 2, we're gonna\u003C/p>\u003Cp>Speaker 1: set of actions. There you go.\u003C/p>\u003Cp>Speaker 0: It's 1 at a time. Yeah.\u003C/p>\u003Cp>Speaker 1: Okay. So that Now we want to start a game and just see if it works. And there's a we're also going to want to look at our directors logs too because we did console log just data. But let's we need to start the game.\u003C/p>\u003Cp>Speaker 0: Let's start Then we can first. And let's grab create new gate. Oh, not create new battle sync, create game, and let's grab escape. Oops. Escape.\u003C/p>\u003Cp>Escape.\u003C/p>\u003Cp>Speaker 1: Which I now regret.\u003C/p>\u003Cp>Speaker 0: K. Add game. Is it in there? There we go. Beautiful.\u003C/p>\u003Cp>Okay. Let's see. Game is running. Something's happening. Not what we want to happen.\u003C/p>\u003Cp>Sure. That's okay. Let's head back over to Directus and see what our logs are saying.\u003C/p>\u003Cp>Speaker 1: Scroll up because, obviously, all the turns happen. Right. Strategy. Boom. We\u003C/p>\u003Cp>Speaker 0: just need to do dot value.\u003C/p>\u003Cp>Speaker 1: That's why. Okay.\u003C/p>\u003Cp>Speaker 0: It's it's returning this. It's the yeah. Request to get variable strategy failed with the response variable not found for key strategy. Hold on. Did I not save?\u003C/p>\u003Cp>I feel like I might not have saved something over on the cycle, which is a necessary step here is to actually save it. Okay.\u003C/p>\u003Cp>Speaker 1: That does sound necessary.\u003C/p>\u003Cp>Speaker 0: Oh, hold on. Maybe this is not that window. Ah, see this? Save. It doesn't actually know that it's there.\u003C/p>\u003Cp>It's my own darn fault. I got too many windows open. Darn you, Andrew. Honestly, I'm pretty sure\u003C/p>\u003Cp>Speaker 1: it's the curse of academics. Every academic I've ever seen or a covering academic. I think you are though current. Correct. Yeah.\u003C/p>\u003Cp>It's it is that. So while we're here, maybe we just straight because this is a DVC variable. Let inside of the extension itself return strategy return dot value. Yeah. And then rebuild, restart.\u003C/p>\u003Cp>Speaker 0: Yep. So we're gonna return\u003C/p>\u003Cp>Speaker 1: This is good.\u003C/p>\u003Cp>Speaker 0: Yep. Dot value. Awesome. There we go. So that should work.\u003C/p>\u003Cp>That should do what we want it to do. Let's rebuild here.\u003C/p>\u003Cp>Speaker 1: You got it.\u003C/p>\u003Cp>Speaker 0: Yeah. We're in the right oh, that's snake. We wanna be dev cycle and not CD. Let's go npm run build. And now we're going to need to restart again.\u003C/p>\u003Cp>Speaker 1: Yeah. Once again, not the case now, but I'm too I'm too nervous just just in case. The upgrade is right in this moment.\u003C/p>\u003Cp>Speaker 0: Okay. And I need to oh, why did I do\u003C/p>\u003Cp>Speaker 1: that? Why did you do that? I\u003C/p>\u003Cp>Speaker 0: don't know. Questions questions have been asked. Okay. So let's see if we can create a rematch. Should work this time.\u003C/p>\u003Cp>Nope. Not working.\u003C/p>\u003Cp>Speaker 1: Well, it's not so much that. That's just the bit we care about because it's our new snake. What we care about is if we go back to the logs\u003C/p>\u003Cp>Speaker 0: Are these the new\u003C/p>\u003Cp>Speaker 1: No. No. No. No. The the log the logs won't tell us anything.\u003C/p>\u003Cp>Go see if the strategy has been saved against the latest game.\u003C/p>\u003Cp>Speaker 0: Oh, that's true. Yes.\u003C/p>\u003Cp>Speaker 1: That's what we're actually testing here. Oh, no. No. No. We're not doing that.\u003C/p>\u003Cp>Sorry. We check the flow logs, the flow logs for start. This is what we're doing. And we check the latest log. We check the dev cycle payload.\u003C/p>\u003Cp>Speaker 0: Ready, set, battle, snake is the snake we're feeding in. Payload up. That's good. I should be feeding everybody up right now. Up, up, up, up, up, up, up.\u003C/p>\u003Cp>Yes. Hey. We did it. That's awesome.\u003C/p>\u003Cp>Speaker 1: Now so now we wanna store the strategy. And then in the move logic, we want to grab this next this game strategy. We want to act on it.\u003C/p>\u003Cp>Speaker 0: I love this. Okay. So we have this here. Now we need to store it in here, right? We're actually Yeah.\u003C/p>\u003Cp>Speaker 1: We just wanna edit the create data.\u003C/p>\u003Cp>Speaker 0: Edit the create data. Oh, in here. Gotcha. Yeah. Because we're already creating it.\u003C/p>\u003Cp>Speaker 1: We just need to add\u003C/p>\u003Cp>Speaker 0: strategy.\u003C/p>\u003Cp>Speaker 1: You're missing a comma on line 3.\u003C/p>\u003Cp>Speaker 0: But I laugh at commas.\u003C/p>\u003Cp>Speaker 1: Okay. Do you\u003C/p>\u003Cp>Speaker 0: wanna hear do you wanna hear a fun do you wanna hear a fun, little story while we're doing this? I'm ready. So I, am teaching at at the university this this term. And, I was talking to my students who are all future teachers about using proper grammar in the posts that they're doing on the WordPress blogs they create. And I misspelled grammar.\u003C/p>\u003Cp>Beautiful.\u003C/p>\u003Cp>Speaker 1: Well, you can't have it all. You could have good grammar or good spelling. So so remember, the value was no not trigger. The returned value from the dev cycle step. And remember, the key was dev underscore cycle or lowercase, so not the dollar sign, was the string.\u003C/p>\u003Cp>So it is just dev underscore cycle, and the value of that will be the string.\u003C/p>\u003Cp>Speaker 0: All right. Let's see if this works.\u003C/p>\u003Cp>Speaker 1: Because that's what was returned out of that step to save. And so let's start one more game. Let's make sure that's being being stored.\u003C/p>\u003Cp>Speaker 0: Oh, cut out there, Kevin.\u003C/p>\u003Cp>Speaker 1: Oh, I actually just stopped talking.\u003C/p>\u003Cp>Speaker 0: I'm not sure. Oh. You you definitely froze.\u003C/p>\u003Cp>Speaker 1: Oh, okay. Fair fair enough. Yeah?\u003C/p>\u003Cp>Speaker 0: Okay. Good. Okay. Perfect. So we ran a game.\u003C/p>\u003Cp>Yeah. That's fine. And then let's head back over to our logs here. And the I mean,\u003C/p>\u003Cp>Speaker 1: the proof is gonna be in the pudding here.\u003C/p>\u003Cp>Speaker 0: No. So But then no\u003C/p>\u003Cp>Speaker 1: no no the options. Strategy up. So if we now go look\u003C/p>\u003Cp>Speaker 0: at the latest Oh, there we go. Yeah. We're there.\u003C/p>\u003Cp>Speaker 1: Yeah. Yeah. So if so if we go just check the content module in the sidebar here in the, module bar far left, let's go look at the latest game and make sure our strategy was saved. That's the top one. Top one.\u003C/p>\u003Cp>Speaker 0: What is Games.\u003C/p>\u003Cp>Speaker 1: What what an easy quick way to do this is if you just hit the plus in the top. Like, if you add a if you add a field, perfect strategy, boom. Nice. Amazing. So now we're gonna go into our move, our new move, which does nothing, which is why our snake is doing nothing, and we wanna respond to that strategy.\u003C/p>\u003Cp>Alright. So So not this one. We we we we we we we Oh, that's right.\u003C/p>\u003Cp>Speaker 0: Yeah. Yeah. We did a strategy one. That's right. Okay.\u003C/p>\u003Cp>Awesome.\u003C/p>\u003Cp>Speaker 1: Snake strategy.\u003C/p>\u003Cp>Speaker 0: And let's do this as a we're just gonna return. Right? So we just need to do\u003C/p>\u003Cp>Speaker 1: No. Because there's a few steps here. So this takes in a move, right? And that move has a game ID. So the first thing we want to do is we want to look up the game to find out the strategy, right?\u003C/p>\u003Cp>So we want to read data first and my confidence has just gone through the floss. So let's close out this for a moment. Can we just look at the logs here and just understand the shape of the data again?\u003C/p>\u003Cp>Speaker 0: All right.\u003C/p>\u003Cp>Speaker 1: So So the payload, body, game, ID. And that should match the ID of the game in our collection that has been created by the start step.\u003C/p>\u003Cp>Speaker 0: So let's see. 4ff ending in 70. Let's see if that is in fact the case.\u003C/p>\u003Cp>Speaker 1: Sure. I I think it should be. Yep. There we go. Perfect.\u003C/p>\u003Cp>So what we want to get out is that strategy. So, so we'll create a new step here, and we can call this get game or something.\u003C/p>\u003Cp>Speaker 0: Yeah. Get game strategy. Let's call it.\u003C/p>\u003Cp>Speaker 1: I'll just get this is gonna get the whole game. So let's just get game. And then permissions full access because I can't be bothered to deal with permissions, collection, games, IDs. Now this is important. We want in squiggly brackets, double squiggly brackets here, trick, dollar sign trigger dot body\u003C/p>\u003Cp>Speaker 0: Body. Yeah. Dot game\u003C/p>\u003Cp>Speaker 1: ID. Game. Yeah. You got it. Dot ID.\u003C/p>\u003Cp>Yeah. And hit enter. And hitting enter just slightly, you know, you have to enter. And just like you're hitting save over there in dev cycle, that's one that gets me.\u003C/p>\u003Cp>Speaker 0: There we go.\u003C/p>\u003Cp>Speaker 1: And this should get the game. So hit save. Let's see. And let's just do this very sequentially. So, yes, let's let's test this again.\u003C/p>\u003Cp>Once again, the value should absolutely be up.\u003C/p>\u003Cp>Speaker 0: Perfect. Options.\u003C/p>\u003Cp>Speaker 1: Permission to pull\u003C/p>\u003Cp>Speaker 0: the games ID. Wonderful. Wonderful. And we get our strategy.\u003C/p>\u003Cp>Speaker 1: Lovely. And the strategy now is what we're gonna check. So now we're gonna do some branching logic. So what we want is, I think, a conditional.\u003C/p>\u003Cp>Speaker 0: Yep. Right? Yep. And we're going to call this strategy.\u003C/p>\u003Cp>Speaker 1: Strategy. Well, let's be more specific because we're gonna check if it is one strategy or the other. So Oh, that's true. Is, you know, strategy, yeah, is up. Although, was was up the value yeah.\u003C/p>\u003Cp>Yeah. Off was the value.\u003C/p>\u003Cp>Speaker 0: Yeah. It's up the value.\u003C/p>\u003Cp>Speaker 1: It's up. Yeah. Perfect. So let's fill in the so I don't think we do wanna fill in the default. Yeah.\u003C/p>\u003Cp>So squiggly like, this is JSON now. So squiggly brackets on the outside.\u003C/p>\u003Cp>Speaker 0: There we go. I love this, by the way. This is such a smart little\u003C/p>\u003Cp>Speaker 1: Except we are gonna have to go edit it. So I know. It's all it's. So trigger change for last because we wanna grab stuff from the last step. Oh, sorry.\u003C/p>\u003Cp>A dollar sign. Yeah. Last. Perfect. Yeah.\u003C/p>\u003Cp>Correct. And we're gonna be checking the strategy. So not category, strategy. Yeah. And when you see whether it is equal to up and it's case sensitive.\u003C/p>\u003Cp>So yeah. Perfect. Wonderful. And hit save.\u003C/p>\u003Cp>Speaker 0: Now, honestly, this is where this is where\u003C/p>\u003Cp>Speaker 1: I mean, nothing would oh, yeah. Suppose we can check. We can check that.\u003C/p>\u003Cp>Speaker 0: Kevin, let's follow, strategy every time we do this, but, no, we won't follow the strategy. Let's see.\u003C/p>\u003Cp>Speaker 1: Oh, wait. Wait. Wait. Nothing it didn't get saved.\u003C/p>\u003Cp>Speaker 0: What?\u003C/p>\u003Cp>Speaker 1: Didn't save the flow. You just refreshed.\u003C/p>\u003Cp>Speaker 0: Oh, did I really?\u003C/p>\u003Cp>Speaker 1: No. Womp. Womp. It's okay. Come on, Andrew.\u003C/p>\u003Cp>Speaker 0: Come on. I mean, to be fair to\u003C/p>\u003Cp>Speaker 1: be fair, there is a little bit of allowing you to do that without prompts. Anyway, anyway, is up Yep. Was the name of the condition, and it's gonna be lost. Last. Yeah.\u003C/p>\u003Cp>Speaker 0: And you call them strategy.\u003C/p>\u003Cp>Speaker 1: You call it.\u003C/p>\u003Cp>Speaker 0: And oh, strategy, and up, and save and save. Up. It's the double save.\u003C/p>\u003Cp>Speaker 1: I get it. I get it.\u003C/p>\u003Cp>Speaker 0: Let's set out. Let's finish this game. And so I figured out the thing that it is here. You actually have to finish the game before it'll let you do a rematch. You have to play the whole game.\u003C/p>\u003Cp>That was the issue we were facing.\u003C/p>\u003Cp>Speaker 1: Good tonight.\u003C/p>\u003Cp>Speaker 0: So we should have it showing up now. Yes. He's up. Payload. Yep.\u003C/p>\u003Cp>Awesome. Is\u003C/p>\u003Cp>Speaker 1: up. Now, this basically will fail if it isn't up. It will it will, like, it will have a validation error, and the whole flow will fail. And this is correct.\u003C/p>\u003Cp>Speaker 0: Yeah. Let's see. Look at you go. All right. Here we go.\u003C/p>\u003Cp>And amazing. Alright. And let's go back over to directors refresh. Look at our logs. Shut up a bunch more.\u003C/p>\u003Cp>Let's go. Oh, oh, something's happening. Oh, I need to actually press the button. Alright. Here we go.\u003C/p>\u003Cp>Oh, option payload validation error.\u003C/p>\u003Cp>Speaker 1: Validation error. Wonderful. And so this is where, we have to be creative with flows and work within some of the limitations, to be frank. So because all all conditions, there is no, like, case statement where you can be like a, b, or c. It is true or it is false.\u003C/p>\u003Cp>It is resolved or it is reject. Right now, our flow simply halts because it goes out that reject path and fails. Right? But what we're gonna do now is we're just going to put, you know, we're going to just put one thing on the reject path, but it isn't failed. It's just the else.\u003C/p>\u003Cp>Right? So it's just an interesting terminology thing there. So we don't, we don't we don't even need this now.\u003C/p>\u003Cp>Speaker 0: Oh, I guess Unless you wanna do can be Well, why don't you wanna do it? Yeah. Let's just do all 4. There's only 4 to do. So\u003C/p>\u003Cp>Speaker 1: So hang on. Hang on a moment. Have you put you've put this on the reject path. Right? Yes.\u003C/p>\u003Cp>Perfect.\u003C/p>\u003Cp>Speaker 0: Yep. Okay. Last strategy down, and we're gonna call this is down. Perfect. Yes.\u003C/p>\u003Cp>And then we're gonna create another.\u003C/p>\u003Cp>Speaker 1: Off the reject path. Off the reject path. So it gets a little bit unwieldly here. So it's not up. It's not down.\u003C/p>\u003Cp>Speaker 0: Strategy. Let's call this last.\u003C/p>\u003Cp>Speaker 1: Copy it out this time before you hit save.\u003C/p>\u003Cp>Speaker 0: Look at you, left. Perfect. Copy. And then one more.\u003C/p>\u003Cp>Speaker 1: So you so you see here how these kind of more convoluted case statements can get a little bit messy in here?\u003C/p>\u003Cp>Speaker 0: Yep. I definitely do. But, is right. Okay. So let's go there.\u003C/p>\u003Cp>Save this. And go to the game. And let's finish this game. Shouldn't take too long.\u003C/p>\u003Cp>Speaker 1: And this one should do the same thing. It should have a validation error after the first one.\u003C/p>\u003Cp>Speaker 0: Yeah. Alright. Oh, yeah. That's true. Oh, no.\u003C/p>\u003Cp>Speaker 1: See if\u003C/p>\u003Cp>Speaker 0: it does. No. No. No. No.\u003C/p>\u003Cp>Speaker 1: That's a lie. Sorry. It will it will go until it fails. Just ignore me. That was incorrect.\u003C/p>\u003Cp>Sorry.\u003C/p>\u003Cp>Speaker 0: Is up. So that was our first one. Is up failed. Must be up, and then it dot stopped.\u003C/p>\u003Cp>Speaker 1: That's not correct.\u003C/p>\u003Cp>Speaker 0: Did I do is up twice? Is left, is up, is down.\u003C/p>\u003Cp>Speaker 1: Oh, no. No. It's connected to read data. It needs to be connected to the reject path of is up.\u003C/p>\u003Cp>Speaker 0: Oh, there we go.\u003C/p>\u003Cp>Speaker 1: Not that one. There we go.\u003C/p>\u003Cp>Speaker 0: Yeah. Yeah. Yeah. There we go. Oh, did too much stuff.\u003C/p>\u003Cp>Disconnect, save, edit, reconnect.\u003C/p>\u003Cp>Speaker 1: Oops. I'm glad. I'm no. No. No.\u003C/p>\u003Cp>Reject. Reject. Yeah. I'm glad we finally got back to that point of just finding that little that little set of actions. I can't believe it got us so, so much.\u003C/p>\u003Cp>But, yeah, I've submitted\u003C/p>\u003Cp>Speaker 0: that as a bug report. Awesome. Alright. Let's refresh this. And we should be now we should be seeing the process that you were hoping for.\u003C/p>\u003Cp>Speaker 1: Yeah.\u003C/p>\u003Cp>Speaker 0: Lots more moves there. So we should be seeing is oh, yeah. Here we go. Awesome. Yeah.\u003C/p>\u003Cp>It's up. Validation error. Validation error, validation error, validation error.\u003C/p>\u003Cp>Speaker 1: Why is there a validation error on all 4? It has to be one of them.\u003C/p>\u003Cp>Speaker 0: Oh, last must be of type object? I wonder if that is not Oh,\u003C/p>\u003Cp>Speaker 1: of course. Last is the last step. We've foot gummed by using last, which I actually spoke about in a previous episode. What we wanna check is, can you click into read data? The read data Yeah.\u003C/p>\u003Cp>Key is called get game. Right? So let's maybe rename this game. So if you go up to the key in the top right of this whole pane, scroll up. Yeah.\u003C/p>\u003Cp>Yeah. And the last will always be the one that came before it. But in this case, we're chaining a bunch. So, like, changes. So we actually wanna change.\u003C/p>\u003Cp>So if we change this to just game, let's keep it nice and short.\u003C/p>\u003Cp>Speaker 0: Perfect.\u003C/p>\u003Cp>Speaker 1: And and, yeah, save. And now we could do it in is up. The is up last is correct, right? But in all the others, it isn't lost anymore.\u003C/p>\u003Cp>Speaker 0: And then we're just removing this, and it's actually just gonna be game. Right? No Correct.\u003C/p>\u003Cp>Speaker 1: Yeah. Correct.\u003C/p>\u003Cp>Speaker 0: Okay. So let's go No,\u003C/p>\u003Cp>Speaker 1: dollar sign.\u003C/p>\u003Cp>Speaker 0: Yeah. Or no dollar sign. Yeah. There we go. And let's call this one game.\u003C/p>\u003Cp>Oh, game. And copy that this time. And I'm quite impressed with where we've gotten here. I feel like episode 4 finally figured things out.\u003C/p>\u003Cp>Speaker 1: Yeah. Alright. Here we go. So Yeah. So now so so let's test that once more.\u003C/p>\u003Cp>And while we're doing that, let's talk about what's next. Because all we're gonna do is execute the most basic strategy, which is to just return, move up, down, left, right. And we're not gonna do anything more fancy here. Right?\u003C/p>\u003Cp>Speaker 0: You would implement\u003C/p>\u003Cp>Speaker 1: is up, not correct. Is down though, is correct, which Awesome. Great. So now out of each of these success paths, now we need to run the strategy, which of course could be a lot more convoluted. Another thing that's worth noting is there is a flow operation, which is to run another flow.\u003C/p>\u003Cp>So you could you could kind of modularize your strategies into separate flows, go off and call those those come back with the response. And then so, you know, there are ways of organizing your flows. It's not just one big mega flow. But it's I think for what we're doing, it's beyond the scope, but that that would kind of be how you organize game logic, I think.\u003C/p>\u003Cp>Speaker 0: Great. Okay. So what are we gonna do here? What are we we're sending this\u003C/p>\u003Cp>Speaker 1: to We are literally gonna return. So run script. So sorry. I run script was near the top. And the reason we're doing run\u003C/p>\u003Cp>Speaker 0: script just to move and then up and down and left. You got it.\u003C/p>\u003Cp>Speaker 1: That's awesome. You got it. Because in the trigger, we said respond with the last operation. Yeah. I love that.\u003C/p>\u003Cp>Speaker 0: All right. So let's just grab that, save, and let's do oh, yeah.\u003C/p>\u003Cp>Speaker 1: Then that's correct. Yep. That's correct. So that's the is up success path. The names and the keys no longer matter, because If\u003C/p>\u003Cp>Speaker 0: we unless we forget which one where it is down, so this one's gonna be down. Save that, and then we're gonna go here. This is left.\u003C/p>\u003Cp>Speaker 1: I'm glad you you've come with me on the up, down, left, right rather than the clockwise.\u003C/p>\u003Cp>Speaker 0: We got there. We got there eventually. We got there. Alright. And then our final one is gonna be right here.\u003C/p>\u003Cp>And then this one's gonna be run script and\u003C/p>\u003Cp>Speaker 1: return You got it.\u003C/p>\u003Cp>Speaker 0: Move right. And let's see if this works. So if this works, our move right now is down. So snake should go down if everything is done the way it's supposed to be. So we've been going up because it's been failing.\u003C/p>\u003Cp>Now we should go down. Alright. This doesn't necessarily mean it's working yet because let's see if we go right. Save. Did we just actually implement this in, like, an effective way?\u003C/p>\u003Cp>This is wild. I'm, like, kind of\u003C/p>\u003Cp>Speaker 1: Hang on. Hang on. Hit play first.\u003C/p>\u003Cp>Speaker 0: We don't know yet. Yeah. What is up?\u003C/p>\u003Cp>Speaker 1: Snake went right straight off the board and died.\u003C/p>\u003Cp>Speaker 0: This is fantastic. I have never been so happy to see a snake die. I mean, last episode, I was really happy because we really wanted our snake to die. But, like, I'm\u003C/p>\u003Cp>Speaker 1: Oh, yeah.\u003C/p>\u003Cp>Speaker 0: I'm far more happy. That's pretty freaking cool.\u003C/p>\u003Cp>Speaker 1: That is cool. But there was one final part, and then I think we're we we can we can call it on ReadySet Battlesnake, at least season 1 of ReadySet Battlesnake, who knows, which is right now we are saying all users that there is no segmentation. There are no different audiences. It's just everyone gets one response. But in the flow creation, in this start game, you know, flow, we can put in a dynamic value there.\u003C/p>\u003Cp>Now, I do not know how this works in dev cycle. Is this something that's viable?\u003C/p>\u003Cp>Speaker 0: Yep. Yeah. Yeah. So hold on. Let's go back over and actually look at this.\u003C/p>\u003Cp>Because you said words and I heard the words, but then I stopped hearing the words. And so let's, let's go and shift this in that flow. And so we could do it in our in our dashboard. Right? We could actually\u003C/p>\u003Cp>Speaker 1: We we we wanna do it in both. So here in the start, right, in the dev cycle operation that we created, if you go in and edit it, you see we put in our as fixed snake. So the question here is right now we're getting I think there's a step missing here, which is we want to register users.\u003C/p>\u003Cp>Speaker 0: No. No. You don't actually so you don't technically need to register them there. Oh, I've gone all blurry. Hold on.\u003C/p>\u003Cp>Hold the phone. Why have I gone all blurry? What's happening? There we go.\u003C/p>\u003Cp>Speaker 1: Alright. I'm back.\u003C/p>\u003Cp>Speaker 0: I'm back. Okay. So what we can do here is this is all just stuff used for targeting rules. So let's say, okay. So you know how if it doesn't see anything, it goes up.\u003C/p>\u003Cp>Right? Correct? Not right. I shouldn't say right. If if it Yeah.\u003C/p>\u003Cp>If it's not seeing anything, it's gonna do that. So let's leave this as ready, set, Battlesnake and go over to the platform here Yes. And say Yes. Go, let's say go left, but only if the user ID contains ready.\u003C/p>\u003Cp>Speaker 1: But but what I'm wondering is, can we get this to a spot? And, like, we can assume the outcome here, like, we'll we'll we'll do this. But what I'm wondering specifically is can we, in 10% of games, maybe this is about the game ID and not the snake ID. So maybe the the name of the property isn't quite right. But can we say in 10% of games or 10% of users, I suppose, can we change it?\u003C/p>\u003Cp>So in that case, when we first feed it the snake ID, which will make the game ID, is that can we do that?\u003C/p>\u003Cp>Speaker 0: Yeah. It's just this. You literally set it to random distribution, and we say that we wanna serve 25% each of these distributions, and it will automatically do this for all users.\u003C/p>\u003Cp>Speaker 1: So we don't need to do anything with this passing a random name or anything? No. I mean, you could do it within that. You could say only the snakes that have ready in it. Yeah.\u003C/p>\u003Cp>Exactly. Do this, you know, so you can further branch that logic down and get a real small selection of your audience. Should we remove that, the first set, the first conditional where you put ready? Yeah. Should we remove that,\u003C/p>\u003Cp>Speaker 0: I guess? Because it's kind of just do all users. Yeah. And now we're just saying that all users random distribution. A random distribution.\u003C/p>\u003Cp>Speaker 1: So if\u003C/p>\u003Cp>Speaker 0: I save this, we should see if we run, say, 10 games, we should see something similar to that. So let's make sure this works. That's good.\u003C/p>\u003Cp>Speaker 1: Oh, that was remarkably easy.\u003C/p>\u003Cp>Speaker 0: It is very smooth.\u003C/p>\u003Cp>Speaker 1: But but there is that other thing still where, like, if we if we register, can we say, no, ignore me. That makes part of that same mistake.\u003C/p>\u003Cp>Speaker 0: All of these things are possible. If you can feed information into the platform, then you can you can do lots of stuff with it.\u003C/p>\u003Cp>Speaker 1: And we've only discussed all of these ways of feeding data in, so we don't necessarily need to go too granular. But, yeah, let's create a ton of games.\u003C/p>\u003Cp>Speaker 0: Okay. So here we go. So let's go first game. So we went left first game.\u003C/p>\u003Cp>Speaker 1: Sure.\u003C/p>\u003Cp>Speaker 0: Let's go create rematch. So we're going left. So that's 2 for left. Let's create a rematch. We should start going a different direction, and we're not.\u003C/p>\u003Cp>Maybe I didn't save. Hold on.\u003C/p>\u003Cp>Speaker 1: Yeah. I was gonna say, did you hit that save button?\u003C/p>\u003Cp>Speaker 0: All users, random distribution, schedule, none. No random distribution. We should be seeing that. That. All users feeding that in.\u003C/p>\u003Cp>25%. K. Let me change this. I just wanna see if something happens. A 100%,\u003C/p>\u003Cp>Speaker 1: 0. Oh, you you you have to make it worth what happens if it exceeds a 100 there?\u003C/p>\u003Cp>Speaker 0: I don't I don't know. I'm curious. Yeah. Yeah. So we should see this.\u003C/p>\u003Cp>Like, all users should ever run a 100% should be there. Random distribution might require something else, but let's see.\u003C/p>\u003Cp>Speaker 1: Yeah. Maybe.\u003C/p>\u003Cp>Speaker 0: No. No. That's right. Yeah. I guess\u003C/p>\u003Cp>Speaker 1: if I may.\u003C/p>\u003Cp>Speaker 0: It must just be the logic that's being used in there. So let's do 5050, and that might be a little bit easier. 50 right, 50 down, and see if that Sure. Does it. That's there.\u003C/p>\u003Cp>It also does say a couple minutes to update. So let's see.\u003C/p>\u003Cp>Speaker 1: Yeah. Maybe that's it. Maybe there's a little cache. Which is fine.\u003C/p>\u003Cp>Speaker 0: Right.\u003C/p>\u003Cp>Speaker 1: As I say, our snakes aren't spawning in particularly in particularly good spots to live. They're not? This isn't feeling unless it is really that thing because you you don't need feature flags to be literally like sub second. Know, there can be a little bit of a of a moment there.\u003C/p>\u003Cp>Speaker 0: Yeah. So this is over, like, a longer period of time. So that might be part of it, but we should see a down eventually,\u003C/p>\u003Cp>Speaker 1: because\u003C/p>\u003Cp>Speaker 0: the logic that's there is technically correct. Oh, you know what it is? No. I'm wrong. Here's the thing.\u003C/p>\u003Cp>I I totally forgot about this. So because it feeds in user ID and we're feeding in the same user ID every time. So if we wanted to change this, it's actually not that bad. So that So this is\u003C/p>\u003Cp>Speaker 1: actually what I wanted. Yeah. Yeah. So users and that makes sense because in the real world, you have a feature flag. You don't want that user getting a random thing every time they reload.\u003C/p>\u003Cp>Speaker 0: Yeah. So ready, set, battle, snake was getting right after Oh, no.\u003C/p>\u003Cp>Speaker 1: No. No. No. Hang on. Hang on.\u003C/p>\u003Cp>Go back. There's a there's a better value to put in here. Let's put in the game ID. Let's put in the because that's random. That's in every new game is a new is a new That's true.\u003C/p>\u003Cp>User in this context. So actually\u003C/p>\u003Cp>Speaker 0: grab game.\u003C/p>\u003Cp>Speaker 1: Okay. Double double, double brackets first, and it will be trigger dot body dot Game\u003C/p>\u003Cp>Speaker 0: dot ID.\u003C/p>\u003Cp>Speaker 1: Game dot ID because that's a random value each time. Yeah. It is. That's good. Yeah.\u003C/p>\u003Cp>Yeah. Yeah.\u003C/p>\u003Cp>Speaker 0: That might that might do it. So we should now see 5050 if it works, which it may not.\u003C/p>\u003Cp>Speaker 1: Mhmm. Mhmm.\u003C/p>\u003Cp>Speaker 0: K? Good. So right on the first one, right on the second one. It's not looking good so far.\u003C/p>\u003Cp>Speaker 1: It's not looking good so far, is it?\u003C/p>\u003Cp>Speaker 0: Oh, there we go. Hey. Hey. There we go. So So\u003C/p>\u003Cp>Speaker 1: that logic that I think that's Andrew, I I I'm I'm both really happy that we got this to work, But I'm also kind of sad because I think I think that might bring us. What do you think?\u003C/p>\u003Cp>Speaker 0: I think I think we have we have eaten into to your evenings for many, many dates.\u003C/p>\u003Cp>Speaker 1: Not for that reason. Not for that reason, but just I think that's I think anything more now is us just in hot like, doing things we've already done, but in this slightly altered context, but that's awesome. So you can really, like,\u003C/p>\u003Cp>Speaker 0: yeah. So smooth. Yeah. I gotta say, like, I when we set out to do this, I knew that we might be able I knew Directus would work. Like, that made sense.\u003C/p>\u003Cp>But when we decided that I was gonna take the lead for the final show and we were gonna do the dev cycle, I was like, I don't know if this is gonna actually work because I tried to implement dev cycle with Directus a lot of ways. So I'm over the moon that this works. And, like, what a cool piece. And it's so fast too. Right?\u003C/p>\u003Cp>You're not having to worry about direct us doing anything. It's all managed externally. Like\u003C/p>\u003Cp>Speaker 1: Yeah. And and part of this is an approach that we chose. And this is a very battle snake kind of thing. It is an approach we chose to make sure that we are we're not combating that 500 millisecond return require return requirement. And that is because we determined a strategy at the beginning of a game, not every move.\u003C/p>\u003Cp>So there is a couple more round trips within directors. We're having every move to go and figure out the game strategy, but this is all internal. These are just little database operations. That's awesome. And then, of course, going back to the last episode, we could now enrich our insights dashboards and saying when this was the strategy, this was the outcome.\u003C/p>\u003Cp>We could use the little global variable drop down to to pick. We could even go off to dev cycle, populate directors with all viable strategies, That was awesome. That was fun. Happy?\u003C/p>\u003Cp>Speaker 0: This was a fun day. I'm I'm so Kevin, I was so it's been a couple episodes since I've been, like, on the keyboard. And, like, you were walking away so pumped after every and I was happy. I was definitely feeling it. But, like, I'm having a good this was a good It's good.\u003C/p>\u003Cp>This was a good day. We got we got things working, and our snake does the things, and other people can now go and use this to meld the world's all the d's, Directus, Devcycle.\u003C/p>\u003Cp>Speaker 1: Yeah. What what do you say, as for as for Directus as a Battlesnake engine?\u003C/p>\u003Cp>Speaker 0: I love it. I think it's super, super smooth. And I love the fact that there's actually, some code that exists out there for building a dev cycle management setup within the Directus platform. So, like, like, use Directus and then implement dev cycle and then play Battlesnake. That's the real that's the real goal here.\u003C/p>\u003Cp>Do Everybody wins. Everybody wins.\u003C/p>\u003Cp>Speaker 1: Everybody wins. Everybody wins. Everyone, thank you so much for joining us. Andrew, thank you for getting on board with this idea to to, you know, have a Battlesnake video, you know, series after after a little while. Thank you audience for joining us for this limited series of Ready Set Battlesnake.\u003C/p>\u003Cp>If you want to learn more about Battlesnake as with all of these episodes down in the resources, you'll find a link to the docs for Battlesnake. It is such a satisfying and fun hobby project for developers at all stages of their career, whether you're just getting started or you are really pushing, you know, your very, very advanced knowledge and it becomes an optimization game. There's something in it for everyone. I'm glad they all worked. Honestly, there were so many points in this where we might have discovered this doesn't work irreparably and the whole thing's ruined.\u003C/p>\u003Cp>And every barrier we came against, there was a solution primarily actually through the ability to build extensions. Yeah. So that is wonderful. Oh, lovely. And a melding of my world as a big time and long time battlesnake lover and running dev rel, at, director.\u003C/p>\u003Cp>So Andrew, once again, thank you so much.\u003C/p>\u003Cp>Speaker 0: You're welcome. And should we do it our one last try? This is our last opportunity? So\u003C/p>\u003Cp>Speaker 1: here we go.\u003C/p>\u003Cp>Speaker 0: What do you mean? What do you mean? You have little faith? This is it. You have a So here we go.\u003C/p>\u003Cp>What do\u003C/p>\u003Cp>Speaker 1: you mean? What do you mean? You have little faith. This is it. You ready?\u003C/p>\u003Cp>Speaker 0: Alright. Here we go. Alright. Do you want to count it down? Should we count it down first?\u003C/p>\u003Cp>No. Hang on.\u003C/p>\u003Cp>Speaker 1: Hang on. Hang on. Hang on. I've got it. Uh-uh.\u003C/p>\u003Cp>Alright. This has been\u003C/p>\u003Cp>Speaker 0: ready. Ready. Set. Set. Battlesnake.\u003C/p>\u003Cp>Battlesnake. Bye for now. Bye.\u003C/p>","Hello, everybody. Hello. I'm sorry, Kevin. Ready yet. You are ready. Alright. Let's try this again. Alright. 3, 2, 1. Hello, everybody, and welcome to ready no. I didn't know where to do that. We're gonna do it. Ready. Ready. Set. Set. Battlesnake. I don't think we're gonna get it, Kevin. I don't think we're gonna say the final episode of Alright. We're gonna try this again. I feel like this is I feel like this is good for the final episode. Let's do this. For the final episode. Let's get this right. Welcome to the final episode of Ready set, set, battle mistake. Mistake. We're close enough. We got there. Close enough. We got there. I'm gonna count it. Alright. So welcome, everyone. Episode 4 of Ready, Set, Battlesnape. I'm Andrew. This person either down below or to my right or left. I don't know where he's he is right now. Direction. Is Kevin, and we are your your snake charmers, your code, your code coaches, your, your fun fans. I don't even know. But we are here today. You are fully overestimating our entertainment value. I I don't know what you're talking about. I feel like our entertainment value can never be underestimated. We are we are more entertaining than we even think. Thank you so much for coming back and checking this out, Kevin. Thank you so much for for coming back again after I kinda threw you, a little bit of a, a little bit of a challenge at the end of our our last episode together. It was tough. But we got there. I'm sure you'll return the favor to me today. Yeah. So if you haven't caught episodes 1, 2, and 3, you should probably go back and check those out. We, kind of we're gonna talk a little bit more about exactly what we've been doing, But, in episode 1, we built a Battlesnake, and we'll talk a little bit about Battlesnake kind of as we go through. Obviously, in episode 2, we implemented, that same logic from our 1st JavaScript starter snake in, Directus. In episode 3, we added a whole bunch more logic to our snake but mostly just to visualize the games that were coming through the platform. And today Kevin, do you know what we're doing today? I I do, but I will let you I will let you share. I think it's your turn. I think it is. I feel like you've been leading the show for the entire time. So it's probably my turn. Also, I feel like should I be let me see. I'm, like, wanting to be more centered. Oh, where are we? There we go. Now I'm feeling good. Okay. Good. Alright. I thought that, like, make make the choice to change where your camera is as you're as you're on on camera. Yeah. Screw it. Why not? Good vibes. Good vibes. So, on today's episode, we are going to be, implementing a new technology that we haven't really looked at, called Devcycle. And so, you now know from the last few episodes about what Battlesnake is. You know what Directus is because you're here on Directus TV. And so now is the time to introduce you to the technology that I work with on a daily basis as a developer advocate at Devcycle, which is a feature flag management platform. So our plan today is to try and add some logic to our, Directus snake to get this, working is maybe a relative term. Getting the the dev cycle platform yourself a lot of scope there. A lot of scope. Working is a relative term. But we're gonna use the dev cycle platform to, add a little bit of, add a little bit of flavor to our snake and give it a little bit of, give it a little bit of control. And that will make a lot more sense in a minute. But Kevin, you look like you have something to say. I I mean, I'm gonna ask the question which I think you're probably gonna work towards, which is like, can we just talk for a moment about what feature flags are and then maybe what you had in mind, and then we launch straight in. I love it. So, a feature flag, if you've never heard of them before, condition conditional statement in your code, that allows for you to, turn features on and off depending on a bunch of targeting criteria. So let's say you are working on a new feature on your platform, or a new feature maybe for your Battlesnake, and you're not quite ready to to roll that out in all of the games, you could add a feature flag. Just wrap it around the code and then through a feature management platform like DevCycle, you can say, for these kind of games, I want you to use kind of this logic. And for these kind of games, I want you to use these this logic. It's a really big part of things like trunk based development. So if you're moving from, like, branch based development where you're sort of, like, having features in branches that are long lived, and then we all had to deal with sort of, like, long lived branches and merging and dealing with merge conflicts. Mhmm. Feature flags are kind of a way to avoid those things just by constantly having your new features in code, and you can kind of decouple the code release from the feature release process. Interesting. Okay. And just to embellish this with my understanding, and you can tell me if I'm wrong, they basically work. So you put these conditional statements in your code, and what triggers those is this feature management platform. And typically, you would do that with some form of either like AB testing where people get allotted into these random buckets or you can be a bit more pragmatic and say 5% of users are gonna have this conditional turned on and therefore get a new feature. And then we will also, like, we'll analyze their behavior as a result and decide whether it works, if people get it, and so on, rather than going it's an all or nothing kind of deal. Right? You got it. You got it. Got it. That's it. Alright. Money. So Great. That is a very ambitious plan that I just laid out, by the way. I I feel as though may we get to a stage where we're able to, like, just determine, what kind of gameplay we're gonna have? That's possible. Maybe not in a 2 hour stream, but, we are gonna get Directus and Devcycle working together. How do I know they work together? Because I've already written a blog post on this separately from Battlesnake, so I got to explore. And so now is the time to use my knowledge for for good and for evil. We'll link the blog post, by the way. We'll link the blog post. There are 2. There's 1 on our blog, that Esther wrote, which uses dev cycle, and 1 on the dev cycle blog, which you wrote, which is direct us. And I'll put a link to both of them in the resources section that accompany this video. Yes. I love it. So, y'all are seeing my screen right now. And so up here, you'll notice that this looks very similar, to, 1, mister Kevin Lewis's screen, although not in the Arc browser. I'm not apparently a strong enough, software developer to use Arc. Not there yet. Once it comes to Windows, I'll start using it. It's it's it's happening. I know it's coming. I'm in a month of this. And then people will full on get this, but we're clearly recording this a day after the last one because they these are the games from the last episode they created about 23 3 hours ago, which is completely legit. We did something a little cheeky before this stream. We in a completely low rim fashion, you're running a director's to project. And I literally was like, here's a sequel database file and here's the extensions. Go run them and here's the creds I used to go log in. We may have to reconcile with that at some point when things break. So there is a chance things won't behave and we'll have to fix that on the fly, but should be okay. I love it. So we have our snake our direct to snake here. It's alive. It works. Not yet. We haven't determined if it works yet, which we probably will have to do. I also have our snake that was running on Replit. So the original, ready set battle snake that we created from the JavaScript starter snake. So Mhmm. I have that snake, but Replit was getting real mad at me today because I was using too much RAM and and compute. So I pulled that down, and put it into this wonderful, repo here. Yes, sir. And you're gonna be bumping your font size up about 5 times. No. We're gonna expect everyone to to zoom in. Exactly. Exactly. That's the look of the look at my spectacles. Everyone has. And so, yeah, let's let's zoom in here a little bit. Alright. So this is the the kind of the original snake that our code is based off. So you will notice here that not all the logic that we implemented over the past couple of episodes are in here. That's fine. This is more just to, like, show us how it's gonna work once we get there, and then we're gonna implement this indirect disk. We're gonna try and implement it in direct disk. But I think maybe the first thing we should do, Kevin, is actually get our Directus snake working. What do you think? I feel like that's a logical first step. And what we're gonna do to make that happen is we're gonna set up ngrok. And so do you pay for ngrok? I don't, but I have ngrok. So, like, I can I can set it up right now, but it'll be ephemeral? Incredibly heavily, like, rate limit to you. Let's pause here. Let's get NGROC set up and using temporarily my auth token, and then we will come back. Great. So this bit, we won't we won't publish. But, yeah, it was actually the one thing we forgot before we started. So let me just figure that out. Yeah. Throw me the key and then I can, Indeed. Alright. And let me just go to engrok now because I was actually logged in earlier. It was working for me in the games that I was playing, but, it but then it gets mad. That's the whole problem here. So what we want here is your auth token. So, here is the engrok command you are going to want to run here in the chat. Yep. Sorry. I'm just then because I'm on, like, old, the command looks slightly different, and that's there. Where are you hiding? Hold on. My windows have all disappeared. Where are you, Kevin? There you are, in chat here. Alright. Wonderful. There's a chat. Here's a command. We're gonna add this auth token. Beautiful. And we're gonna head back over here to our terminal and oh, nice. There we go. Paste. Beautiful. Auth token saved, and let's run Nice. This. It's good. I set up ng croc earlier. I knew Yeah. Yeah. Absolutely. Beautiful. Alright. Good. So I think we can actually snap back now, and Yep. It's up to you what you want on the screen. We could be here if you want. Yeah. I mean, I mean, if you've never used ngrok before, I mean, we talked about this in episode, I guess, 2. We were looking at this. I have the free version. Kevin's got one that's less rate limited, so we're using that one. But it's super easy to install. Like, just homebrew it, and then you add your auth token to ensure things are happening. But, anyways, we've got it running right now. And then my direct to snake over here is should technically be working. Now, Kevin, I'm really curious. You should technically be able to go and run a game on yours, and see if it works. And I'm interested I think Are you running directors? Apparently, I am not. I think I might have closed it. Nope. I'm running it. Yep. We're all good. Where is it running though? That's the real oh, you know it's running in another one. Do you have another I do. Versus code do. Window. There it is. Awesome. So we're gonna watch these logs here. We're gonna bump Shall we whack the font size up there as well? So I'll add ready set battlesnake and hungry bot once again to an 11 by 11 board. I'll hit start game. Yeah. And there we go. It's coming through. Awesome. We're working. And you're like, but Andrew and Kevin, we can't see the games, and therefore, this isn't fun and enjoyable for us. Us. Fear not. Because I think actually an easy way to do this so we can keep ready, set, battlesnake in your repo. Can you make ready, set, battles cake public so that I can add him to my Yeah. I can. Yeah. My battlesnakes. Ready set battles cake. We named it that. So it had a k instead of an n because it was the Kevin version of the snake as an FYI, and it is now public. Awesome. And so if you're ever in the Battlesnake platform, really easy. All you gotta do is just search for snake. Ready, set. Should find him skate. There we go. That's what we want it to look like. And so now we've got them in there. And now we're gonna see this game going on, and it should be all of our logic and direct us. Something's not happening. Something's unhappy here. Let's see what it is. Womp womp. I love that we tried and then we failed. Could you remove the what the old one password, pop up? I certainly can. Okay. There we go. Oh, no. Nope. Same thing happened again, actually. Create Rematch. So let's think it through. What is going wrong here? It could be that our flow isn't running correctly, but it should be. Yeah. Like, it's going off. It's triggering the flows. It's getting a 200, which means it's getting a response, and it's returning. So what is going what is going on? What is going on? We haven't gotten Because it was randomly breaking last time too. At the end of session 2, at the end of the second episode, our snake was, like, overpowered. Like, it was so good, and then it started to crumble. It started to grumble, last time. Let's take a look at our logs. We have got some logs in here. Holy moly. We do. Let's look here. So is there anything going on in here? We've got move now. Original. Yeah. So I think we've got something going on there. Some so what do we have here? Yes. Feeding a lot of things there. False, false, false, false. Oh, no safe moves. Avoid head to head. I think it might be in there. False, false, false, true, true. Yeah. I think the issue is in Which we've already determined that we think is the case. So you know what we'll do to keep ourselves moving? If you close the pane, why don't we just avoid that entire operation and just take avoid body straight to determine move? Avoid head to head is a, you have to just hit the little edit in the top right of the Oh, of course, we do see. Yeah. And hopefully yeah. If we just do that and hit save, yeah, just yeah. Oh, we're back. We are back. We're back. It's alright. We're there. I have a belief that if you just hit the save button enough times, we're gonna be able to resolve. I'm pretty sure that's the definition of insanity. I think it is too. We're gonna switch things up for just a minute. Yeah. And save this, and that didn't work. And then we're going to go back over here. And then we're going to go back here. And we're going to see if that worked. Oh, disk. It was a bug. It was a bug, by the way. And it had an issue has been opened on this bug. So there you go. Maybe just discard the changes and just we can have Alright. Let's see if we can create another game, see if this will do it. I mean Okay. The oh, nope. Still doing the same thing. Still so moving out of bounds, logic is there. It's definitely happening in 2 minutes ago. Let's get You have to refresh. Sorry. Alright. So let's go here, and let's see again. So determine move is still where things are, yeah, getting hung up. Oh, and you're saying avoid head to head is where everything went to force when they're logically, it should not been happening. I think so. Maybe that's what it is. Let's go avoid head to head options. There we go. False, false, false, false. Payload. Avoid bodies. Okay. So so you know what we're gonna do? We're gonna jump into avoid head to head, and we're just gonna return the inbound date. Like, we're not gonna do anything. Alright. Let's go in there and do that. So here we go. Right. Because that's clearly where it were. Yeah. We're gonna literally delete everything apart from line 2 and 30. So everything in the middle there, ditch it off. Doesn't matter. Can we not just oh, we can't comment. Really? I think no. So just but it's fine. We don't need it. This is this is the last episode. There we go. I also have a copy of it, so it's fine. Ah, success. Alright. So let's save this and see if it works. There we go. And now let's go and try and recreate a game again. Because if that's the step that was causing grief. It was. Yep. Do you remember when I wrote all that really elegant code and it worked fantastically? Turns out not so much. Well, it's alright. It worked fantastically until it didn't. And that is the key to all the programming. It's all good until, you crash production. Okay. That makes me feel better that we've identified the issue and removed it for now. Okay. So ready, set, battlescape is working, and this is really the key here. It's still doing the job that it needs to do. So we're gonna make, we're gonna do something a little bit different now. We are going to actually be, having we're gonna bring ready set battlesnake back from the dead, in a new world. So let's, let's kinda go over here. So this is good. Directus is working, and now we're gonna we're gonna ignore that Directus even exists for the next little while. And we're actually gonna head back over to Who needs to pay rent? Who needs to pay rent? Not me today. And I wanna look at, wanna look at our code here. And so is this okay for you, Kevin, or is it still too small? Yeah. Maybe take up another yeah. Maybe that maybe that's good, I think, for people watching. Awesome. So in here, not a lot has changed from when we were dealing in replit. So we still have our server dotjs, which is just an express server. We've got the 4 endpoints, the sort of root endpoint, start, move, end, and then we've got a bunch of stuff in here that's kind of, yeah, talking about what it's returning. We've got a read me here, which explains just how this works, which we don't really care about right now. And then we've got our package. Json. I haven't added anything new to here except for this new dependency, which is the dev cycle, SDK. And so this is necessary for us to implement dev cycle. If we want to do it this way, technically, we could do all of this via our management and bucketing APIs, but I think it's cleaner through the through the SDK. And then, there's some small changes. There's another file that's here, and we'll maybe call this in just a second. And I'll explain how I got to this file, which was not fun this morning when I was prepping for the show. So, originally, in here, we had our run server. We had our info endpoint. We had our start and end endpoints. And then we had this move endpoint, which actually had some really good logic inside, that if we go and ignore all of this stuff, here was our original logic. And I'm gonna uncomment all of this stuff. Oh, I'm gonna try and uncomment all of this stuff if I actually uncomment the right section. There we go. So this was our original logic. And I'm actually think just to make sure that this is working because, yeah, I've changed this so it wasn't async now, which was part of my issue earlier. I'm gonna save this. So this was sort of our dumb snake that we first started with. So Not dumb. Yeah. But, like, dumber. And so you can see here all of our steps have kind of been implemented here. The base steps here, same thing that's going on over in Directus. Disk. So let's see if this is working. I'm gonna run this express server. I think it's node index dotjs. So this is gonna run How are you gonna expose this as a Battlesnake if we're already running ngrok for directors? Oh my good guy. Also, updated cache direction to down. It is constantly running that caching. Interesting. That's fun. So the caching wasn't originally there, Andrew. So Wasn't. And please tell us about the cursed file. So we're gonna ignore the cursed file for a minute, and then we'll get to the cursed file. But let's see if we run this right now. Technically, I should be able to run 2, right, through engronk? I'm not convinced, but we'll we'll find out. Okay. What port does Directus run on? Not that one. K. Well, let's see if this works. No. It works. It's there. No. No. No. No. No. No. No. No. No. No. No. Locally, but you still have to expose it. Yes. So can you see if one shows up in your, is it list it'll show up on your ngrok dashboard on your end if it's running. I didn't even know there was an ngrok dash I didn't even know there was an ngrok dashboard. We'll see. But yeah. Let's take a look. So behind the scenes, I'll actually pull this up just so people can see what this looks like. So when you go to the ngrok dashboard and you're logged in, if I am logged in, so you have, like, this authentication and set up stuff. And then you also have this tunnel section. And in the tunnels, I think it's tunnels where it actually shows you, yeah, what are currently online in your account. Account. And so you should see you should see I've got one. You've got one? K. So this is not doing what we wanted to do. Wait. Wait. Wait. Wait. No. No. Yeah. And the one is you. Oh, it is? Yeah. Started 10 minutes ago. I'm not running anything locally. That's that's Directus, though. That's why. That's directives. Okay. So here's what we're gonna do. And now we need to try and run it a second time, but I'm not convinced. We can shut down directives for right now because we don't need it as we're just kinda demonstrating how this works in the index dot JS. So we're gonna shut down Directus for a second, and then we're gonna run it over on our other one. Alright. So we've shut down Directus, and let's go back over to index dot j s. Let's cancel this out and awesome. Restarted that. What is the address that I'm wanting to go to, Kevin? Can you paste it somewhere for me? I don't understand what you're asking. Never mind. I But, also yeah. I've But you're not running ngrok yet. Right? Oh, that's right. I have to rerun. No. I am running it. You need to rerun it. Exactly. Rerun ngrok. Yeah. Oh, there we go. Got it. Bad things happening. Okay. Let's go But this time, not 8,055. Yeah. Yeah. 8000. Alright. Here we go. Beautiful. Alright. Alright. So let's go over here and see if this is working. Beautiful. Alright. So this is running now. So if I go back over here to account settings oh, not account settings. My battle snakes. I'm gonna go ready set battlesnake, and we're gonna leave a JavaScript, and we're gonna have this on nothing, no platform today, and then we're gonna change our server URL. Yes. Like that. And now if I go in and add ready set set, Battlesnake to a game, create game. And ready to set Battlesnake. Remove the Octa thing in the top right? It's very annoying. My goodness. All of the prompts. And then we are going to see if this runs. Okay. Good. Basic logic, not running into himself, not running into walls. Good. So he's working. Alright. But I haven't implemented the logic for dev cycle yet. So let's go look at what's going on. So we've got the moves that are feeding through there. That's awesome. That's what we want. I'm gonna actually turn this off for a second, and then turn it back on. No. I'm not gonna turn it back on because it's gonna constantly need to be caching. Okay. So, that's our basic code. Right? We're good. We're there. We're not here for basic code today. We are here for confusing broken code that will cause many problems and will make our heads hurt. You're really teeing this up, and I'm horrified before we've started. Don't worry. You and me both. So let's look at the new logic that I have in here, which is not great logic, but, I'll explain it. So, what we're doing is every time the move endpoint is called, this move function is called. Yes? Everybody should track that so far. So what I'm doing here is I am calling a, this get cache direction function, which is basically every second or millisecond. I'm not sure. I think it might be millisecond. It's calling the dev cycle, platform and asking what move should I be getting for whatever user is on the platform right now. This will all make sense when you kinda see the logic, but, basically, it's it's pulling the dev cycle platform to ask it what move you should be making as a snake. Does that make sense? Yeah. I think your face Unfortunately, this isn't even remotely the direction I thought we could go in. No. Don't worry. We're not gonna we're not gonna manually we're not gonna manually do this. We're I just wanna demonstrate the, like, how it actually functions. And so this is good. Mhmm. And I actually you can do it mid game too as long as your your click game is is on point. Oh, man. Which we can do today because there's 2 of us here. So Oh my god. We're gonna go and actually look in this cache dotjs file. So here's our cache.js file. Couple things happening here. First thing, we're importing the server SDK that this dev cycle from there. Then we're initializing our dev cycle, client in here, despite it being a server application, we're calling it a dev cycle client. This stuff is That's fine. That makes sense. Yeah. This stuff isn't super important, the cloud bucketing and enable EdgeDB. It is and isn't, but really the important thing here is that we're initializing the dev cycle client. We're creating this cached direction, which is setting the default move for our snake to go down. So, basically, if, the first time, if there's been no caching, if this has never run before, it's gonna go down. And then, yeah, within this function, we're feeding in the user variable. So you have to feed this in every single time that you, that you do anything with the external platform. You have to feed to the user. You can feed in custom properties, which we may try and do today, where you can, like, base it on different information that you may have. But this is the idea here. And then once we've got this dev cycle, client established, we're then going to look for a specific variable that exists in the Dev cycle platform. Think of a variable as, like, the feature flag that you're looking to pull. So whether you're wanting to see on or off, we're actually pulling a string. So we're pulling for the direction for this user. What should this user be seeing if we're looking for the direction variable that's in there? And then we've got if we do end up having a preferred direction. Yeah. Because where's my preferred direction defined? I think it was defined over here. Right? Yes. There we go. So, preferred direction. So, basically, it's saying that if there is the DevSecLE client being established and it finds that that direction variable exists, then we're gonna look for that preferred direction variable that we had defined over an index.js. And if it returns anything but none, it's going to put a new cache direction value in there, and then it's gonna log that it's updated the cache direction. Otherwise, it's gonna give you an error. And then if we go down here, yeah. We're basically updating this every 10 it's not ten seconds. This is, like, every 10 milliseconds. That's very, very often. Very often. So a1000 is one second. Yep. And then I gotcha. Yeah. So this is our code that we're doing here. Basically, we're just constantly pulling the dev cycle platform to, like, get the latest updated stage. So there's a little bit of a delay here, but it works pretty well. I will believe it when I see it. Gonna be honest, mate. Okay. Alright. You're like, this is garbage spaghetti code. What are you doing, Andrew? It's not like it's not the code. It's just this isn't how I would have. Can I give you maybe an idea of how else we could have implemented this sort of code? Yeah. Let's yeah. Let's go. We literally just have, like, branching logic in the flow. And based on, like, there's the snake gets an ID for every game. So you can enroll them and then, like, 10% of the time or whatever, they get thrown into, like, strategy 2. And strategy 2 could be it become it just goes left all the time. Like, it doesn't matter. Yeah. But you could implement branching logic. Right? So we are and that is what we are gonna do. So you're not wrong with that. I just wanna show how this works. That's fine. Okay. Then we can get into the logic. So Okay. That's our logic here. So, basically, we're calling this every 10 seconds, and then it's gonna if it's updating, it's gonna show us this using cache direction, and then it's gonna do that. And just to clarify, the whole reason you've created this notion of a cache direction is so we're not gonna go to dev cycle every single move. We will go a 100 times a second instead. So what's what's interesting is because we're basically running an async method here, there's a delay and devs battle sync doesn't like there being any sort of a delay. So I had to I completely got it. So you have to have some I got it. Yeah. You've gotta do the polling yourself. I think you probably if I was a better programmer, I could probably figure it out, but this was like No. No. I I I get it now because you could call dev cycle every time, but you're not gonna be able to respond within 500 milliseconds confidently. Yes. So for that reason, instead, we will catch the last direction it gave you. Exactly. I got it. I got it. That makes sense. Fine. You're I'm with you. And it's interesting. The reason I figured out that this was working is so if you notice here, in the logs, so we actually it returns move 117 right. What I was seeing in my logs was it would be like move 01746328. And so it was taking time to actually do the the the calculation. So anyways Which is no good for the snake. It just won't work. Yeah. And so I'm gonna leave this I'm gonna leave this at, at 10 milliseconds for right now, now only because I'm running on my own device, and I think your end grunt won't get upset at me. And because I know that it will demonstrate how this works. The downside here is I actually, Kevin, will need you to be the one that is starting no. Because nobody's gonna be able to see it. Okay. We're gonna see how fast Andrew can click things is what we're actually gonna see. So we're gonna start this again. So if everything's working properly over here, I've uncom oh, I've gotta uncomment this. Let me cancel that again. Let's uncomment that import statement. I think all the rest of this logic has already been commented. Awesome. So now if we run this yeah. So we're basically checking this, like, every 10 milliseconds. It's kind of crazy. I don't fully know why I don't think it's running every time it shouldn't be. I'm unsure logging every time it's making your terminal choke. Here's my question though, is we're not calling move all the time. So I'm confused why That's a good point. Get cache direction is being called every single time because it shouldn't be. It should just, like, literally only be called when moved unless I've got a game running somewhere. Do I have a game running somewhere? You shouldn't be. No. I don't even know how you would check that. Yeah. I don't know either. But, like, if I look at this okay. Request made. Oh, are you the squiggly brackets? Yes. The destructuring. I shouldn't have done that. Alright. So, yeah, it should not be running. It should be running every time. What is going on here? It must be something in this actual cache file. Let's see what's going on here. Import dev cycle, initialize dev cycle, let cash direction go down, update cash direction, import dev cycle. Yeah. Because you're set into it here on line 33. So that's happening regardless. That's happening the moment it's imported. That's not linked to that function running. I don't think it's that much of an issue. I'm gonna change this to, like, a 100 because what's that? That would've that was gonna be my only suggestion is maybe just knock it knock it down a bit. Okay. Yeah. Anyways, we're gonna do this. We're not gonna do a lot of this, because it's gonna be less important. But, we're gonna set this running, and then we're gonna go and actually take a look at the game. So here we go. So we're basically changing this direction, and I'll actually actually, let me cancel that for a second. And I'll go to the dev cycle platform so I can show you. I already set this up. Yeah. Yeah. Yeah. That'd be good. Here are the dev cycle form. Once I log in once it logs in come on, dev cycle. What are you doing? Oh, there we go. Logged into the wrong one. So we actually just launched a, a bunch of new example apps, and so I've been playing around with those. But there's no way to log out without, getting rid of the the demo mode. So, that's how we're gonna log out. I feel like it's like a user retention is great if the user can ever log out, get a life hack. All right. Let's close these again, see if I close this. And let's try app.devcycle.com again. And I think this is gonna load. Yeah. There we go. Awesome. Log in with my work count. It's gonna bring us over to the dev cycle platform, all of our production stuff. We're not gonna mess with our production stuff today. Do it. Do it. Let your intrusive thoughts win. So I did it at, at KubeCon. I actually we made a bunch of live changes while we were doing demos, and our entire engineering team got very unhappy with us. And so I think we're ready to set Battlesnake. And I've got this features area here. So I've got a feature that I've set up called my choose move feature. And this is Could you, zoom in, sorry, 1 or 2 times? No. That's okay. The small text is quite small. We're good. Nice. Thank you. Perfect. Yeah. So we've got our choose move feature. This isn't super important for us right now because I've only got one feature. And then I've got this direction variable that I have there, and I've defined 4 different variations, left, right, up, and down. These are string variables that are in there. And then in my targeting logic, I'm basically saying who to serve what or what to serve to what users that are feeding into that SDK. So here, I'm basically saying serve all of the users down, but that's also my default. So I actually want to be serving to see if this is working and actually talking right. I'm going to change this to serve everything right. And if this is working now, when I turn on that Battlesake server, I should be seeing everything in this updated cache direction except for the first one showing as right. So let's see if that works. There we go. And even the first one did. So we've got the changes happening here. And now if I go back over to the platform, change this to up and save and then take a look at my logs. And so it's like real time changes that are happening in there that you're able to kind of differentiate what's going on. So the interesting thing here is that, technically, platform to control your snake in real time. It's a terrible idea. That is literally the like, like, we've seen people play Battlesnake live using, like, games controllers. And even that, you're like, well, there are some questionable choices here. This is like drop down and then save. So but I'll show you something inter I'll see if I can get this to work, and I'll just do a I started using, there we go. So I started using, rectangle, and Sure. I can't believe that I've never used it, anything like that. Oh, yeah. I use Raycast for it, and I have keyboard shortcuts. Oh, look at you. Which is really, really nice. So let's grab ready set battle sneak in here. And we're gonna go start gaming. Just ready set battle in here. And so you'll see it's not gonna work right now because it's not running. So he's going up. So we know it's not down. There's no conversations that are happening right there. So since we know that we've set our game mode to, let's change it to left so we know that something's working here, and then I'm gonna start my server. And now if I create a rematch, should go left. Awesome. So we know that it's working. Sure. Alright. So now I'm gonna do the thing that is, like, the test of Andrew's ability to click things really fast. I'm gonna create a rematch. And then in, like, real time, I'm gonna click save. And there should be a switch if the board is big enough. But, actually, I need a bigger board because that will give me more time for things to go wrong. Let's go 19 by 19. Add, ready, set, Battlesnake. Start game. Oh, I shouldn't have done that. Anyways, it's fine. You've done that. You you moved moved. Do do do do do. No. It's too late. The game already runs as soon as you click create rematch, so I gotta be a super fast. Alright. Here we go. So right now, he's set to go left. I wanna change it so he goes Don't worry about hitting play. Just hit create rematch, hit save, and we can see it in the playback that it's happened. Alright. Here we go. There we go. Oh, and he's going left first. So literally, the worst example that ain't gonna work. Before you create rematch before, you wanna yeah. Prime. Prime. Oh, yeah. Change it. So right now, we're going down. So we're gonna get it. So we're gonna go first, we'll be going down, and then we should go right as long as we know where it, as we long as we know where it spawns. Okay. This should work, actually. This should be good. So here we go. Should go down first, and then at a certain point, go right. Oh, not fast enough. We're gonna try this again. I tell you, it did it does work. You just gotta have the the right movement. Andrew, I full on believe you. You you don't have you're not proving anything. Don't worry. Come on. Really? Your your taste. Oh, did we see that? We did it. We did it. So here's the thing. Right? So we changed it from right to up. So we're going right. We're going right. We're going right. And then suddenly, we start going up. And that's there's no logic in here that says, dearest, dearest ready set battlesnake, avoid that wall. This is all just, Andrew changing that logic in real time. And you can see if we go to the logs there, which are gonna be, like, super overrun now, not that logs, these logs. But you can see here in our terminal there we go. Yeah. So we're updating our cached app, updated cache direction app. And so you can see that there's, like, the change that happens in here. Yeah. Oh, and this is the interesting thing too here. Right? Is you actually see so most of the time, it's updating cash Kevin, are you impressed? Are you blown away by my amazing implementation of this? I am so blown away. Thank you so much. I feel like my life has been genuinely enriched in the last 39 minutes of recording. Thank you, Andrew. I don't know what I would do without you as a friend, a mentor, a colleague. Thank you. Thank you. Good. Yeah. And that's it. Episode over. We're done. Episode wrapped. Thanks. So, that was just like this is I feel like in every good episode, in every good coding experience, you should do something that's totally useless and makes absolutely no sense so you have the knowledge and skills to do something that actually makes sense. But there are some things already set up. Right? So we've got the dev cycle, like, prod forgive my terminology if I use the wrong terminology. But we've got the project set up, we've got the variable, we've got the options, we've got the ability to change it and hit save. We have a client, you know, we have like an ID for this specific feature. So, you know, we have all the parts, and we can just apply it in Exactly. Different ways. So what we're gonna need to do here is I'm going to so we're gonna forget that all of this ever happened. We're gonna go shut off ngrok, for a second, and then we are going to go back to our direct us instance, and we're gonna start direct us up again so we can start to have some fun. And we've got all of our code here. Why We do. Are you It's it's it's the other it's the other Visual Studio code that's in the back. Yeah. Are you talking about? What? Hold on. Alright. We'll doc, and then we'll go back over here to engrok, and let's get this running. Oh, I'm running the wrong one. There we go. Nice. Wonderful. And let's go over to here, which I should actually have open in my window. Oh, and we don't need this for right now. So let's open that and make that bigger. And we should be able to go to here. Wonderful. I am gonna need to log in again, I think. No. No. No. I think it'll be fine. No. I need to log in again. Oh, you're gonna need to log in again. Battlesnake@lws.iohunter2. Glasses us what I set this up with. There you go. Okay. So here is our logic. We removed a bunch of stuff from in there. Great. Our snake is working again. Should be working again. Logs are all there. I feel less bad about all the logging that I was doing of every time that we were calling Dev cycle now that I see these Exactly. Thousands of We actually you know, this was I mean, all the dates, like, visible elsewhere. Just today, we actually recorded, or we we did a live request review, which is our show where we or it's our live event, which then becomes a show on director's TV a week later, where we go through feature requests to talk about them, and today's was all about flows improvements. And one of the big things is the ability to change the retention of those logs. I like that. Because, yes, they get they get chunky. They do. So, what I think we are going to do, and I liked the idea that you were talking about before, Kevin. I think let's do 2 pieces of logic, and we're gonna we're not gonna delete all of this stuff. We're just gonna kinda remove it for right now, and then we'll reconnect it. And I think we'll have just right now 2 different blocks. 1 of them that returns right, one of them that returns left for moves, and we'll have some branching logic in there. How does that sound to you? Let's create a new flow for this, and then we could just hook it up to the extension, rebuild it. Like, I think that might be a good a good shout. Let's do that. So let's call this snake, cycle. Alright. And Great. We're just gonna leave that. All of that's good. Just next. And webhook or webhook. Yeah. Okay. And then get, I think. Host. Not the get. Not I had a 5050 shot there, and I got it wrong. There we go. No async calls either. Because because we'll get because we'll get a post request from the Battlesnake platform. Yeah, data of last operation safe. And then what we wanna do here is grab that URL. Yeah. I think yeah. Perfect. Go go to the code editor. Go to the extension called snake. Yeah. I think so. And then in our source index dot JS, and we just wanna update this. Right? For move for move. The move one. And just to not have to do the round trip, we'll change the for local host 8055. But hit paste. That'll give us the ID of the trigger and everything. Yep. And then, yeah, we'll just change that for HTTP, not HTTPS, localhost secure my how to secure my URLs. Kevin, who who are won't work. Phone's bloody work, man. Actually, it does. No. No. It does. I'm pretty sure. I'm pretty sure ngrok automatically anyways, I don't I don't need to Not not on not on this one. Yeah. Yeah. But yeah. Yeah. Yeah. But not on the local not on local host. If you were doing it via ngrok, but then we're gonna have to do another round trip to ngrok for what? It runs locally. Smart. Okay. So we've got our trigger there. This is our move. So now this is all feeding through to the place that we want. Now we've got to rebuild the extension and restart directives. Npm run build? Yeah. From the from that extensions directory. So let's create a new terminal in here. Yeah. CD extensions. Yeah. You got it. And CD directives Extension. Extension snake. Jake and m You got it. Build. You got it. And then restart And then we just Oh, because cannot. Okay. This is fine. This is like macOS going overboard. Open up your open up your settings on your Mac. Security, I think it is. Yeah. We may not be able to do that just because of the security settings. Security No. It was there. It said privacy and security. Security settings. No. Just yeah. There you go. Allow anyway. It's right there. It's right there. Fsevents.net. Yeah. Yeah. Cool. Yeah. Great. Alright. Let's try this again. You got it. Beautiful. Okay. Lovely. And then restart and then restart the Docker container. Fixed in directory version 10.9, which we have not upgraded to right now. There you go. Hooray. And now you're good. Okay. So let's head back over to Directus, which is Yeah. Somewhere. It exists. It is in the world on this device. Too many windows. Directus. There we go. Okay. So here we go. So this should be talking here. I'm actually gonna go start a game just to make sure this is working. So let's go, play.battlesync.com. Let's go create game. And I think yeah. Does it still save recently used? It doesn't. That's so sad. Used to do recently saved escape. There we go. And if this is working, we should see a log showing up here. Don't look. Happen. Just no. That's fine. We don't it's probably not gonna work work, but it should show a log here. We have 10 logs. I love it. Awesome. Yeah. Okay. Method post. Yes. So that's 10 moves that it took to pass away. Perfect. Logic is coming through there. Accountability. Awesome. Okay. It's working. So now we're gonna do some branching logic in here. So we're gonna go and condition. And we're gonna worry about the condition afterwards, but let's call this dev cycle response. Oh, it's not here. Well, hang on. Not in not in a condition. 1st, we need to go get data and then we run conditions on the results. So I'm assuming we can just make I'm assuming with DevCycle, just an API, we can make an API request, right, just a raw API request. So you want the webhook request URL, one there. Perfect. And first, we need to go get the data, then we'll do the conditional logic. So this might be a moment to look at the dev cycle docs. Yeah, that would make sense. We should probably Yeah. And there will be a moment here where we'll we'll redact it for for the recording, but you're gonna have to authenticate yourself. So you're probably gonna need to put in, like, an API key or token. Can you create multiple inside of your dev cycle? That's a good question. We're gonna experience this together. Yeah. I haven't played around with the yeah. So we're gonna do a little bit of this together, and then we're gonna not do a little bit of this together. So let's head over there. So jumping into the dev cycle docs, APIs, management API. This is to create features and variables. I don't think it returns anything. We actually want the bucketing API. So this is used to fetch on HTTP. Yes. Yes. Yes. Unique identify the user. Definitely, this bucketing API. Get all the variables. Get all the features. Post events. Get variable by key. Okay. This is what we're looking for. So let's head over to bucketing API, and we are going to have to get our key Wait. What what my key? Yeah. Sorry. What's the endpoint we wanna hit? So we are going to do get variable by key for user data, I think. Let me look here. Private platform SDK type. Missing SDK. Get all variables. What is that gonna return? Is that gonna return the status? What's our what's our end goal here? Our end goal is to We wanna just get the value for the variable. So it's very similar to what we're dealing with in our SDKs. To the user ID to start. Yeah. But everything is hidden behind our bearer token. So I'm just gonna find where all of this is hidden. So key type variation, variation in variation key, response examples, get all features by key post events. No. Fetch No. I think get all fee I think I think you were right. Get all features by key for user or get all variables by key for user data. And this will no. Because this will return Here it is. Yeah. Here it is. The value. Fantastic. That's what we're that's what we're after is the value. So, we're gonna go offline for one second as I go and get all of my authentication stuff sorted out, and, then we will be back with our API stuff. Sounds good. Okay. So For what for what it for that's worth, we could totally have kept it going, and we could just redact it, adjust that slither slither and time. Keep it going. Yeah. Let's do that. Alright. We're back. Yeah. We haven't gone anywhere. We thought we were gonna go somewhere. This is interesting too because we if an so, technically, our management and bucketing API are actually using the same stuff. I need to update our docs because we should actually have this curl command in, both, and they're not both there. I guess most people are just assuming we're gonna be using that. So, I'm gonna go and, in a different window, actually, open up, my OAuth token using the client ID. Okay. So I need to go over and what do I need here? Client ID and client secret in my dashboard. Alright. So let's go over here and yeah. I don't think Yeah. Showed anything. I'm a I'm a little bit fuzzy on how you authenticate. Let me show you. Okay. So what we do here is alright. You're seeing my screen now. Hey? Yeah. So and, again, I will fully admit that I have only messed around with our API in a very limited way. So, basically, we are doing a curl request with our client ID and client secret. We can actually obviously do this same post request in Directus, and we're just using our client ID and client c. We can't do it directly and directly. I've just spotted it. The type the encoding type for this, for this request is a URL encoded form, but that request URL, you know, operation that's built in only sends JSON. And they're just you don't work around this. Like, you can't do it. However, I'm not too too sad because you can, of course, do it using a custom endpoint. It's not quite what we wanna do, but could we do or building a little custom operation. So maybe that's what we do in order to to do that round trip. It's not the ideal, like you know, it means we're gonna have to spend some time doing a little bit of extension development. Like, just spotted that. Is that is that the only way to do it? Yep. Yeah. We have no real public API stuff, which is interesting, but also so what's interesting so we are having so we're working right now on some webhook stuff. So you can technically get when, certain changes have been made, like an audit log. You can read that through a webhook, but you can't actually read what people's values are. Because that would that would be so good because on a webhook, you could trigger a separate flow, store indirect just like your cache Yeah. And then do stuff based on that. But that's completely fine. So we could do this or if we're building a a little custom, operation, we could just straight up use the SDK. Yeah. So let's do that. And then we've already got the code there. So, yeah. Okay. Let's, are we gonna do this here inside of our code, or are we gonna do this over on the platform to get started? Where are we gonna I don't under let let's let's build an operation for flows as an extension. And the goal of that operation is to, right now, just get a fixed, you know, a fixed response from from the API. So, no, we don't do it like this. We actually use the extensions SDK CLI, which will boil the plate a whole extension for us. Alright. We're gonna delete that bad boy. Alright. Let's open a new terminal here. Open up a new terminal. You got it. And we'll go into the extensions folder. Alright. Oh, into the extensions folder. Extensions folder. Yeah. Yeah. Yeah. Sorry. Yeah. Yeah. Exactly. Exactly. And then how do I pull up the docs? Or are you just gonna Yeah. Docs. I mean, I could tell I could tell you this one, but let let's let's do the docs. Let's let's have the authentic experience with someone who doesn't live in this every day. There we go. Alright. Let's head over. And we're gonna see this is gonna be my challenge. How long does it take Andrew to find out what I'm looking for? Alright. So when direct to stocks, we're in extensions. We're in device extensions. Zoom in. We're going to zoom in. We're going to go extensions. We're going to go, developing extensions and we're gonna go extension services and introduction. No. That doesn't look like what I'm looking for. Maybe Very good. Very very good little piece of feedback there. Go into the fundamental section, the one you haven't opened out of the 3, the last one, and creating extensions. Perfect. We're just gonna use this. Alright. Let's go back over here and run this. But I I do see that. I do see that path you took. I'm glad to know that. Discussed it with one of our colleagues just yesterday. So yeah. Like, when things work oh, oh, I need to change my note version. It's upset with me. Wait. What? Oh, it's just warnings. Really? Oh, yeah. Whatever. Sure. Me. That looks what do you wanna create? Operation, please. Oh, there it is. Hidden down in that list. Choose a name. And they so they must start so they must start as of this release. I think it might be gone in 10.9 or it'll be gone in a soon release in a upcoming release. Directus dash extension dash whatever you want. Dev cycle is fine. JavaScript because I don't play with TypeScript. I respect that. Dependencies. Yes. Scaffolding it. I love this. This is so smooth. I like a I like a good CLI. Good. Great. So now we have if you go into the extensions drop down, you have a new extension called dev cycle inside of source. We have API dot JS. We have App JS. App JS is for all of the configuration. So it's all the UI part. It's all of the options when you create a new one, you have that little pane that pops out. It's the information that will show on the on the card in the overview where you connect to all the nodes. That's the overview there. And then the API dot JS is the node code that will actually be executed in the background. Right? So we're gonna go and I think right now what we wanna do is actually grab, just so we're not having to worry about this later. We're gonna grab our code from here. So not going back and forth, because this is basically all the stuff that I need. Yeah. Great. And we're just gonna put you down there and comment you out so that things don't get upset with us. Sounds good. Alright. Perfect. So let's save that, and let's do an install. So I do have to have to go into this folder to install, slash extensions. Directus extension CD. Extension. CD. Cycle. Not we're not we're gonna put it at the end and see what happens. No. Start. Start. Start. Start. For a moment, I was like, am I about to learn something? No. There's there's very little learning that will be There's very little learning. I got it. I got it. And then we gotta go over to because we're gonna install the, the SDK. We're gonna go over to the dev cycle docs. So we'll move back over to docs, and let's head back over to the dev cycle docs, docs.devcycle.com. And we're gonna head over to our SDK section, our node JS SDK You got it. Installation. And let's grab this because who uses yarn, NPM or bust? I've become so opinionated in my coding Alright. So that means that our import statement should work now. Actually, is this import or is this require? I This is import. Yeah. I thought so. Alright. Let's move you to the top. Alright. Perfect. So step 1 complete. We're done. Time to go home, everybody. End of episode. So a couple of things that are just important before we continue. There is an ID here called custom. Let's rename that, and then we have to reflect that ID in the App JS and the corresponding App JS. I think dev cycle is a perfect ID. So we just want those 2 to match, first of all. Very good. And let's, while we're here, give this a name. Dev cycle, this is the nice friendly name that shows up when you pick your operation type. Sure. Yeah. Then are we gonna ask the user for any configuration up front? And I'm gonna say that maybe we do. Maybe we wanna collect the snake eye like, the the the ID. Right? Are we gonna do it? Are we gonna have time to do that? I mean, can we give it just an arbitrary ID, or does do you have to register users inside of the dev cycle system first? Like, how does that work? So you would need to feed a specific user's information. Yeah. Let's call a snake name. Yes. I think that's a great idea. Yeah. I was a snake ID. Snake ID. So let's do that. And at first, we'll just hard code it, but later, you can put dynamic values in here in flows. So instead of text, let's just change text to snake. So there's a friendly name. Yeah. Yeah. Perfect. There's a friendly name. Yes. Is that correct? Is it? I changed this from tests from is this yeah? Yes. That's all good. The options need to change too. That's the part. So that that's the overview that will show on the card. Cool. Whatever. It's the options we care about. That's the pain that comes out and you select all the options. So in here, the field, the field will be called snake. And that's what corresponds with the snake in the overview. That's the friendly name. It will still be a string. It'll be full of input. Great. Save. Now we'll go over to API dot JS, and there's text here. We'll change text for snake. So that says taking in the snake value and now we're going to execute some code. But before we do this, I think that's everything on the UI side done. We want to initialize this client and we'll want to initialize this client between lines 13. So everything in your yeah. Great. So, we're going to ignore this because we're not actually going to I'm actually I'm interested to see how this works. Once we have done this, we're going to change some things up eventually. But we're not gonna worry about that right now. So let's grab Oh, which was straight upon commenting everything. That's that's crazy. I mean, nothing could go wrong because nothing's really running yet. So this is good. This server is gonna change. I'm gonna leave these. I don't actually know if we need these, but I'm not gonna question it. If it works before, our goal is to take what worked and apply it here. So we're not gonna make any changes to the logic here. Yeah. Exactly. So we are initialized there now, and now we need to pull all of this stuff up above. And I think we're not gonna need that get cached direction elsewhere. I don't think we wanna necessarily play with that right now. And at the very least, we wanna make sure that we can get data from dev cycle in a flow Exactly. And then pass it on. But if you were having an issue where it's taking a little while to do that round trip, there's a pretty good chance that we're gonna hit that here too. Okay. So let's go here. Awesome. Alright. So here we go, dev cycle clients. So we're established here. We've got this cash direction. Again, I don't actually think we need this anymore because we're gonna be calling it sort of once to determine what's gonna change. We're not gonna be calling this again and again and again. Right? We're not gonna be calling this every move. Is that our that's our focus. Right? I mean, we were. Yeah. Do we we were gonna call it every move? Oh, this is all in the wrong endpoint. This actually wants to go in the start game, doesn't it? Once you're in the start game and then for the game, we determine the strategy and then we execute on it. This is okay. Let's keep rolling now. And then before we've done it with yeah, exactly. Precisely. So let's really let's let's keep this light. In fact, I think doing everything in a separate function may not even be what we Exactly. Want too much here. So I'm gonna pull all of this out of this update cache directory. Hang on. Hang on. I think the only bit we care about is this dev cycle client variable. I think that's the only bit we want. We also care about this user thing. So that's where that's going to snake. Yeah. But that's going to that's already that's coming in from the snake value above. So we're gonna get to feed that in. Yes. Yes. Yep. I guess it has to feed a very specific way. It has to be a very specific object that's feeding in, but maybe you're right. Let's let's see. Let's see. I think I think it'll be okay. So so if we if we go down to where the actual flow handler is, which I think is down. So inside of the handler, instead of the console log, snake is a string. So what we want to do here is we want to make on line 41, we want to make this an async function. Right? Yep. No. As in as in we wanna change just before the the snake. Oh, I see. We wanna make it async, then we'll just use a sync yeah. One more to the left. One more to the left. Goodness gracious. And we're gonna get rid of you. You don't need to be there. And then and then inside, what we wanna do is is run that run that SDK method, but we can do it with a wait instead of then just to clean it up in this context. So if you scroll just very slightly up oh, yeah. We'll ditch something small. Yeah. We're just gonna do this. We're not gonna need to do this call. Perfect. Let's just work the day. Grabbing this. Yeah. But only 17 18 to start. Oh, yeah. You're right. Yep. Because we're doing a sync await. Yep. Yep. Absolutely. So we want that. That goes straight in there, within within a wait at the beginning of line 33 and a store this in a variable. Right? Yeah. Let's call this strategy. Beautiful. Love that. And then user is now gonna be I mean, if you wanna grab lines 13 to 15 just to get the structure, but really it's just an object with the user ID and snake. Yeah. Yeah. Exactly. Let's see what happens. And then yeah. So we're gonna Other way. We're gonna define this above. Yep. We can assign this above. Awesome. And we're actually gonna feed this in. User ID here is gonna be snake. So we're gonna feed in snake here. Snake? Yeah. I think that should work. I think that should work. But we don't know what comes back in strategy yet, so we're gonna log it. Perfect. And then we're going to return strategy. Beautiful. And that's actually add some comments there so we know it's being logged. Strategy. Oh, can I show you can I show you a quick tip for this, which I think you'll really like? Love a quick tip. So remove that remove that string in the comma from the console log. So it's just the word strategy again. Yep. And then highlight all of the word strategy, just the word strategy, and then surround them with squiggly brackets. And now that is an object where the key is strategy and the value is strategy. So when it logs, you get strategy, colon. Yeah. It will be an object. So that is just a real shorthand if you if you just want to quickly tell what something is and you have just a variable key. You know, sometimes I enjoy JavaScript, but only when people that enjoy JavaScript are doing JavaScript with me. Okay. So technically, this should this should do all of the things that we want it to do, I think. Yeah. And so this is wanna change this to hold on. Oh, sorry. Go ahead, Kevin. No. No. No. Go ahead. So I'm just saying here so here, we're actually gonna go and create an so this is a direction variable. We're gonna create a strategy variable and kind of put 2 strategies. We're gonna do, like, hungry and tailchase or something like that. I mean, we we're not gonna implement those. So why don't we do left or right? That is a very smart decision, Kevin. So let's head back over to dev cycle here, and we're gonna get out of there, go into our features again. Into our choose move, it doesn't really matter again what this feature is called. We're gonna create a new variable. We're gonna call this strategy. We're gonna set this as a strict technically, we could just do Boolean, but we'll do strategy. And because of the way that we have this set up with, like, these 4 different options of left, right, up, down, we're gonna actually go ahead and kind of move this around. So we're gonna call it, this one's gonna be left, and this one's gonna be right. Actually, we'll just leave it all exactly the same. Down, up. But it's just called strategy this time. It's called strategy now. Yeah. Sure. Alright. Amazing. Look at that. We changed literally nothing. I love that I did a thing that was completely unnecessary. We could have just kept the stream. Just kept the same. That's fine. That's fine. We we okay. So we're logging this and we I think I think this works, but what we want to do here is is what do we wanna do here? We now wanna hook this up to a start. Yes. So we need to do we need to do a new end. And we need to change the data model to, have this extra, like, strategy string, which I think is amazing because then even in our insights dashboard, we can be like, when using this strategy, this is the win rate. Like, it's actually really lovely. Now we're done in code for right now, though. Right? So I think that we can No. We're not? Build this one. Build this extension. I think we're gonna need to change which flow is being hit on start. I think we need to create a new flow. We need to create a hang on. Oh, metadata. Hang on. Hang on. Is this are these errors or are these suggestions? I think this is all about and has been rewritten. I have a sneaking suspicion this is inside of the SDK. There might be no module in But it said done. Yeah. This may And it says and has been rewritten. All of these have resolutions. I've not seen the error before. I have a sneaking suspicion it's in the dev cycle and the way we're rolling up and bundling code, not a problem. If it works, it works. Not sure if it works. We'll figure that out. So what we actually wanna do is create now another Flow. Flow. Do we need to create another item? No. No. We don't. We can alter our existing start endpoint. Yeah. So yeah. But you've built this. You need to restart Directus. Yeah. Okay. There we go. We're gonna restart Directus. Oh, I need there we go. Let's restart here. Oh, wrong one. Just ignore the fact that we're rebuilding it again. Ignore all of those errors, everybody, and go to the right ones. To me to me, they read as warnings. That's the spirit. Alright. And that should be running again, and we're gonna close that down. We're gonna get the right thing open now. And I think we were here. Let's mark this out. Absolute chaos. I literally can't get behind your window management. There is no there is no window management that is occurring. There is nothing. So, there is one other thing we want to do just before we hook all this up, which is we want to go into the data model. And for a gain, we want to, just add a string strategy. Right? Yep. Yeah. Lovely. And then just because it makes can you make it half width, please? Sure. You use you you could do it with the little 3 dot things on the side come out of this pane, the little 3 dot thing, and then half Got it. Got it. We got there. Alright. So that's in there now. Awesome. Let's head back into flows. Oh, that's not the right spot. Back into flows, and we're gonna change this one. I wanna change the name here before I forget. Can I change that? Yeah. I can. Yeah. Yeah. Back here. Yeah. Yeah. Yeah. Sneak strategy. Awesome. Nice. Perfect. And then, yes, start. And now we want to see if this new thing can so we extract the snake count, and then we create data. And we basically want to create one there in the middle, which hopefully won't pose too many issues for us. Let's build So no. No. Not on the on the success path. Great. And what do we do in our custom operation? Right? Yes. There is at the bottom. Look at that. It even says dev cycle. I think that's a good name to keep for it. I like that. I also like that it converts camel case into, what's what's this? What's the that is called I wanna say hang on. I will I will tell you this because I actually have it inside of my raycast case. That is called that is called, coming up, camel case. That's camel case? This is camel case. Oh, no. Sorry. Sorry. That is I'm I'm I've picked a ridiculous string to test against. So it had, like, it had, like, hang on. That is called, scrolling through, Pascal. Is it snake or is it Pascal case? Snake case. It's called snake case. What is snake? Enough. That's a case. There we go. That is appropriate. It's almost like I knew this, and I wanted to bring it up. Okay. And for a sneak ID, we can actually put in anything. We're just Oh, encoded. Yeah. We're just gonna put, like, a really basic actually, no. Let's call this ready, set, Battlesnake because there's some cool stuff you can do with your targeting rules, including include. So let's just set that. I mean, remember, that can be a dynamic value from the first step. It can. But for now, we'll But we can we can yeah. Absolutely agree. So inside of the dev cycle step now sorry. Can we just go in? That was the only option. And so dev cycle is gonna return some data. Yeah. All of that happens in the background. So we don't know whether it works yet. So now we wanna redo the nodules. Yeah. Oh, yeah. We just wanna move this to here. Right? Yeah. And this is where we seem to keep having a bad time. Awesome. Thank you, Directus, for doing exactly what we were expecting you to do. On behalf of the core team, you are very welcome. So what is our issue in here? What is our issue? But remember, we had this all the way from the first episode, and we just kind of wrote like, there was a specific set of moves we did, and it worked. Okay. That works. I think it might have been this. I hit edit again. Yeah. And then bring it back and then bring it back. No. What if I just disconnect that altogether? K. That works. We're getting there. And then and then do it again now? Edit and just do that last one? Come on. There we go. There we go. We got it. 2, we're gonna set of actions. There you go. It's 1 at a time. Yeah. Okay. So that Now we want to start a game and just see if it works. And there's a we're also going to want to look at our directors logs too because we did console log just data. But let's we need to start the game. Let's start Then we can first. And let's grab create new gate. Oh, not create new battle sync, create game, and let's grab escape. Oops. Escape. Escape. Which I now regret. K. Add game. Is it in there? There we go. Beautiful. Okay. Let's see. Game is running. Something's happening. Not what we want to happen. Sure. That's okay. Let's head back over to Directus and see what our logs are saying. Scroll up because, obviously, all the turns happen. Right. Strategy. Boom. We just need to do dot value. That's why. Okay. It's it's returning this. It's the yeah. Request to get variable strategy failed with the response variable not found for key strategy. Hold on. Did I not save? I feel like I might not have saved something over on the cycle, which is a necessary step here is to actually save it. Okay. That does sound necessary. Oh, hold on. Maybe this is not that window. Ah, see this? Save. It doesn't actually know that it's there. It's my own darn fault. I got too many windows open. Darn you, Andrew. Honestly, I'm pretty sure it's the curse of academics. Every academic I've ever seen or a covering academic. I think you are though current. Correct. Yeah. It's it is that. So while we're here, maybe we just straight because this is a DVC variable. Let inside of the extension itself return strategy return dot value. Yeah. And then rebuild, restart. Yep. So we're gonna return This is good. Yep. Dot value. Awesome. There we go. So that should work. That should do what we want it to do. Let's rebuild here. You got it. Yeah. We're in the right oh, that's snake. We wanna be dev cycle and not CD. Let's go npm run build. And now we're going to need to restart again. Yeah. Once again, not the case now, but I'm too I'm too nervous just just in case. The upgrade is right in this moment. Okay. And I need to oh, why did I do that? Why did you do that? I don't know. Questions questions have been asked. Okay. So let's see if we can create a rematch. Should work this time. Nope. Not working. Well, it's not so much that. That's just the bit we care about because it's our new snake. What we care about is if we go back to the logs Are these the new No. No. No. No. The the log the logs won't tell us anything. Go see if the strategy has been saved against the latest game. Oh, that's true. Yes. That's what we're actually testing here. Oh, no. No. No. We're not doing that. Sorry. We check the flow logs, the flow logs for start. This is what we're doing. And we check the latest log. We check the dev cycle payload. Ready, set, battle, snake is the snake we're feeding in. Payload up. That's good. I should be feeding everybody up right now. Up, up, up, up, up, up, up. Yes. Hey. We did it. That's awesome. Now so now we wanna store the strategy. And then in the move logic, we want to grab this next this game strategy. We want to act on it. I love this. Okay. So we have this here. Now we need to store it in here, right? We're actually Yeah. We just wanna edit the create data. Edit the create data. Oh, in here. Gotcha. Yeah. Because we're already creating it. We just need to add strategy. You're missing a comma on line 3. But I laugh at commas. Okay. Do you wanna hear do you wanna hear a fun do you wanna hear a fun, little story while we're doing this? I'm ready. So I, am teaching at at the university this this term. And, I was talking to my students who are all future teachers about using proper grammar in the posts that they're doing on the WordPress blogs they create. And I misspelled grammar. Beautiful. Well, you can't have it all. You could have good grammar or good spelling. So so remember, the value was no not trigger. The returned value from the dev cycle step. And remember, the key was dev underscore cycle or lowercase, so not the dollar sign, was the string. So it is just dev underscore cycle, and the value of that will be the string. All right. Let's see if this works. Because that's what was returned out of that step to save. And so let's start one more game. Let's make sure that's being being stored. Oh, cut out there, Kevin. Oh, I actually just stopped talking. I'm not sure. Oh. You you definitely froze. Oh, okay. Fair fair enough. Yeah? Okay. Good. Okay. Perfect. So we ran a game. Yeah. That's fine. And then let's head back over to our logs here. And the I mean, the proof is gonna be in the pudding here. No. So But then no no no the options. Strategy up. So if we now go look at the latest Oh, there we go. Yeah. We're there. Yeah. Yeah. So if so if we go just check the content module in the sidebar here in the, module bar far left, let's go look at the latest game and make sure our strategy was saved. That's the top one. Top one. What is Games. What what an easy quick way to do this is if you just hit the plus in the top. Like, if you add a if you add a field, perfect strategy, boom. Nice. Amazing. So now we're gonna go into our move, our new move, which does nothing, which is why our snake is doing nothing, and we wanna respond to that strategy. Alright. So So not this one. We we we we we we we Oh, that's right. Yeah. Yeah. We did a strategy one. That's right. Okay. Awesome. Snake strategy. And let's do this as a we're just gonna return. Right? So we just need to do No. Because there's a few steps here. So this takes in a move, right? And that move has a game ID. So the first thing we want to do is we want to look up the game to find out the strategy, right? So we want to read data first and my confidence has just gone through the floss. So let's close out this for a moment. Can we just look at the logs here and just understand the shape of the data again? All right. So So the payload, body, game, ID. And that should match the ID of the game in our collection that has been created by the start step. So let's see. 4ff ending in 70. Let's see if that is in fact the case. Sure. I I think it should be. Yep. There we go. Perfect. So what we want to get out is that strategy. So, so we'll create a new step here, and we can call this get game or something. Yeah. Get game strategy. Let's call it. I'll just get this is gonna get the whole game. So let's just get game. And then permissions full access because I can't be bothered to deal with permissions, collection, games, IDs. Now this is important. We want in squiggly brackets, double squiggly brackets here, trick, dollar sign trigger dot body Body. Yeah. Dot game ID. Game. Yeah. You got it. Dot ID. Yeah. And hit enter. And hitting enter just slightly, you know, you have to enter. And just like you're hitting save over there in dev cycle, that's one that gets me. There we go. And this should get the game. So hit save. Let's see. And let's just do this very sequentially. So, yes, let's let's test this again. Once again, the value should absolutely be up. Perfect. Options. Permission to pull the games ID. Wonderful. Wonderful. And we get our strategy. Lovely. And the strategy now is what we're gonna check. So now we're gonna do some branching logic. So what we want is, I think, a conditional. Yep. Right? Yep. And we're going to call this strategy. Strategy. Well, let's be more specific because we're gonna check if it is one strategy or the other. So Oh, that's true. Is, you know, strategy, yeah, is up. Although, was was up the value yeah. Yeah. Off was the value. Yeah. It's up the value. It's up. Yeah. Perfect. So let's fill in the so I don't think we do wanna fill in the default. Yeah. So squiggly like, this is JSON now. So squiggly brackets on the outside. There we go. I love this, by the way. This is such a smart little Except we are gonna have to go edit it. So I know. It's all it's. So trigger change for last because we wanna grab stuff from the last step. Oh, sorry. A dollar sign. Yeah. Last. Perfect. Yeah. Correct. And we're gonna be checking the strategy. So not category, strategy. Yeah. And when you see whether it is equal to up and it's case sensitive. So yeah. Perfect. Wonderful. And hit save. Now, honestly, this is where this is where I mean, nothing would oh, yeah. Suppose we can check. We can check that. Kevin, let's follow, strategy every time we do this, but, no, we won't follow the strategy. Let's see. Oh, wait. Wait. Wait. Nothing it didn't get saved. What? Didn't save the flow. You just refreshed. Oh, did I really? No. Womp. Womp. It's okay. Come on, Andrew. Come on. I mean, to be fair to be fair, there is a little bit of allowing you to do that without prompts. Anyway, anyway, is up Yep. Was the name of the condition, and it's gonna be lost. Last. Yeah. And you call them strategy. You call it. And oh, strategy, and up, and save and save. Up. It's the double save. I get it. I get it. Let's set out. Let's finish this game. And so I figured out the thing that it is here. You actually have to finish the game before it'll let you do a rematch. You have to play the whole game. That was the issue we were facing. Good tonight. So we should have it showing up now. Yes. He's up. Payload. Yep. Awesome. Is up. Now, this basically will fail if it isn't up. It will it will, like, it will have a validation error, and the whole flow will fail. And this is correct. Yeah. Let's see. Look at you go. All right. Here we go. And amazing. Alright. And let's go back over to directors refresh. Look at our logs. Shut up a bunch more. Let's go. Oh, oh, something's happening. Oh, I need to actually press the button. Alright. Here we go. Oh, option payload validation error. Validation error. Wonderful. And so this is where, we have to be creative with flows and work within some of the limitations, to be frank. So because all all conditions, there is no, like, case statement where you can be like a, b, or c. It is true or it is false. It is resolved or it is reject. Right now, our flow simply halts because it goes out that reject path and fails. Right? But what we're gonna do now is we're just going to put, you know, we're going to just put one thing on the reject path, but it isn't failed. It's just the else. Right? So it's just an interesting terminology thing there. So we don't, we don't we don't even need this now. Oh, I guess Unless you wanna do can be Well, why don't you wanna do it? Yeah. Let's just do all 4. There's only 4 to do. So So hang on. Hang on a moment. Have you put you've put this on the reject path. Right? Yes. Perfect. Yep. Okay. Last strategy down, and we're gonna call this is down. Perfect. Yes. And then we're gonna create another. Off the reject path. Off the reject path. So it gets a little bit unwieldly here. So it's not up. It's not down. Strategy. Let's call this last. Copy it out this time before you hit save. Look at you, left. Perfect. Copy. And then one more. So you so you see here how these kind of more convoluted case statements can get a little bit messy in here? Yep. I definitely do. But, is right. Okay. So let's go there. Save this. And go to the game. And let's finish this game. Shouldn't take too long. And this one should do the same thing. It should have a validation error after the first one. Yeah. Alright. Oh, yeah. That's true. Oh, no. See if it does. No. No. No. No. That's a lie. Sorry. It will it will go until it fails. Just ignore me. That was incorrect. Sorry. Is up. So that was our first one. Is up failed. Must be up, and then it dot stopped. That's not correct. Did I do is up twice? Is left, is up, is down. Oh, no. No. It's connected to read data. It needs to be connected to the reject path of is up. Oh, there we go. Not that one. There we go. Yeah. Yeah. Yeah. There we go. Oh, did too much stuff. Disconnect, save, edit, reconnect. Oops. I'm glad. I'm no. No. No. Reject. Reject. Yeah. I'm glad we finally got back to that point of just finding that little that little set of actions. I can't believe it got us so, so much. But, yeah, I've submitted that as a bug report. Awesome. Alright. Let's refresh this. And we should be now we should be seeing the process that you were hoping for. Yeah. Lots more moves there. So we should be seeing is oh, yeah. Here we go. Awesome. Yeah. It's up. Validation error. Validation error, validation error, validation error. Why is there a validation error on all 4? It has to be one of them. Oh, last must be of type object? I wonder if that is not Oh, of course. Last is the last step. We've foot gummed by using last, which I actually spoke about in a previous episode. What we wanna check is, can you click into read data? The read data Yeah. Key is called get game. Right? So let's maybe rename this game. So if you go up to the key in the top right of this whole pane, scroll up. Yeah. Yeah. And the last will always be the one that came before it. But in this case, we're chaining a bunch. So, like, changes. So we actually wanna change. So if we change this to just game, let's keep it nice and short. Perfect. And and, yeah, save. And now we could do it in is up. The is up last is correct, right? But in all the others, it isn't lost anymore. And then we're just removing this, and it's actually just gonna be game. Right? No Correct. Yeah. Correct. Okay. So let's go No, dollar sign. Yeah. Or no dollar sign. Yeah. There we go. And let's call this one game. Oh, game. And copy that this time. And I'm quite impressed with where we've gotten here. I feel like episode 4 finally figured things out. Yeah. Alright. Here we go. So Yeah. So now so so let's test that once more. And while we're doing that, let's talk about what's next. Because all we're gonna do is execute the most basic strategy, which is to just return, move up, down, left, right. And we're not gonna do anything more fancy here. Right? You would implement is up, not correct. Is down though, is correct, which Awesome. Great. So now out of each of these success paths, now we need to run the strategy, which of course could be a lot more convoluted. Another thing that's worth noting is there is a flow operation, which is to run another flow. So you could you could kind of modularize your strategies into separate flows, go off and call those those come back with the response. And then so, you know, there are ways of organizing your flows. It's not just one big mega flow. But it's I think for what we're doing, it's beyond the scope, but that that would kind of be how you organize game logic, I think. Great. Okay. So what are we gonna do here? What are we we're sending this to We are literally gonna return. So run script. So sorry. I run script was near the top. And the reason we're doing run script just to move and then up and down and left. You got it. That's awesome. You got it. Because in the trigger, we said respond with the last operation. Yeah. I love that. All right. So let's just grab that, save, and let's do oh, yeah. Then that's correct. Yep. That's correct. So that's the is up success path. The names and the keys no longer matter, because If we unless we forget which one where it is down, so this one's gonna be down. Save that, and then we're gonna go here. This is left. I'm glad you you've come with me on the up, down, left, right rather than the clockwise. We got there. We got there eventually. We got there. Alright. And then our final one is gonna be right here. And then this one's gonna be run script and return You got it. Move right. And let's see if this works. So if this works, our move right now is down. So snake should go down if everything is done the way it's supposed to be. So we've been going up because it's been failing. Now we should go down. Alright. This doesn't necessarily mean it's working yet because let's see if we go right. Save. Did we just actually implement this in, like, an effective way? This is wild. I'm, like, kind of Hang on. Hang on. Hit play first. We don't know yet. Yeah. What is up? Snake went right straight off the board and died. This is fantastic. I have never been so happy to see a snake die. I mean, last episode, I was really happy because we really wanted our snake to die. But, like, I'm Oh, yeah. I'm far more happy. That's pretty freaking cool. That is cool. But there was one final part, and then I think we're we we can we can call it on ReadySet Battlesnake, at least season 1 of ReadySet Battlesnake, who knows, which is right now we are saying all users that there is no segmentation. There are no different audiences. It's just everyone gets one response. But in the flow creation, in this start game, you know, flow, we can put in a dynamic value there. Now, I do not know how this works in dev cycle. Is this something that's viable? Yep. Yeah. Yeah. So hold on. Let's go back over and actually look at this. Because you said words and I heard the words, but then I stopped hearing the words. And so let's, let's go and shift this in that flow. And so we could do it in our in our dashboard. Right? We could actually We we we wanna do it in both. So here in the start, right, in the dev cycle operation that we created, if you go in and edit it, you see we put in our as fixed snake. So the question here is right now we're getting I think there's a step missing here, which is we want to register users. No. No. You don't actually so you don't technically need to register them there. Oh, I've gone all blurry. Hold on. Hold the phone. Why have I gone all blurry? What's happening? There we go. Alright. I'm back. I'm back. Okay. So what we can do here is this is all just stuff used for targeting rules. So let's say, okay. So you know how if it doesn't see anything, it goes up. Right? Correct? Not right. I shouldn't say right. If if it Yeah. If it's not seeing anything, it's gonna do that. So let's leave this as ready, set, Battlesnake and go over to the platform here Yes. And say Yes. Go, let's say go left, but only if the user ID contains ready. But but what I'm wondering is, can we get this to a spot? And, like, we can assume the outcome here, like, we'll we'll we'll do this. But what I'm wondering specifically is can we, in 10% of games, maybe this is about the game ID and not the snake ID. So maybe the the name of the property isn't quite right. But can we say in 10% of games or 10% of users, I suppose, can we change it? So in that case, when we first feed it the snake ID, which will make the game ID, is that can we do that? Yeah. It's just this. You literally set it to random distribution, and we say that we wanna serve 25% each of these distributions, and it will automatically do this for all users. So we don't need to do anything with this passing a random name or anything? No. I mean, you could do it within that. You could say only the snakes that have ready in it. Yeah. Exactly. Do this, you know, so you can further branch that logic down and get a real small selection of your audience. Should we remove that, the first set, the first conditional where you put ready? Yeah. Should we remove that, I guess? Because it's kind of just do all users. Yeah. And now we're just saying that all users random distribution. A random distribution. So if I save this, we should see if we run, say, 10 games, we should see something similar to that. So let's make sure this works. That's good. Oh, that was remarkably easy. It is very smooth. But but there is that other thing still where, like, if we if we register, can we say, no, ignore me. That makes part of that same mistake. All of these things are possible. If you can feed information into the platform, then you can you can do lots of stuff with it. And we've only discussed all of these ways of feeding data in, so we don't necessarily need to go too granular. But, yeah, let's create a ton of games. Okay. So here we go. So let's go first game. So we went left first game. Sure. Let's go create rematch. So we're going left. So that's 2 for left. Let's create a rematch. We should start going a different direction, and we're not. Maybe I didn't save. Hold on. Yeah. I was gonna say, did you hit that save button? All users, random distribution, schedule, none. No random distribution. We should be seeing that. That. All users feeding that in. 25%. K. Let me change this. I just wanna see if something happens. A 100%, 0. Oh, you you you have to make it worth what happens if it exceeds a 100 there? I don't I don't know. I'm curious. Yeah. Yeah. So we should see this. Like, all users should ever run a 100% should be there. Random distribution might require something else, but let's see. Yeah. Maybe. No. No. That's right. Yeah. I guess if I may. It must just be the logic that's being used in there. So let's do 5050, and that might be a little bit easier. 50 right, 50 down, and see if that Sure. Does it. That's there. It also does say a couple minutes to update. So let's see. Yeah. Maybe that's it. Maybe there's a little cache. Which is fine. Right. As I say, our snakes aren't spawning in particularly in particularly good spots to live. They're not? This isn't feeling unless it is really that thing because you you don't need feature flags to be literally like sub second. Know, there can be a little bit of a of a moment there. Yeah. So this is over, like, a longer period of time. So that might be part of it, but we should see a down eventually, because the logic that's there is technically correct. Oh, you know what it is? No. I'm wrong. Here's the thing. I I totally forgot about this. So because it feeds in user ID and we're feeding in the same user ID every time. So if we wanted to change this, it's actually not that bad. So that So this is actually what I wanted. Yeah. Yeah. So users and that makes sense because in the real world, you have a feature flag. You don't want that user getting a random thing every time they reload. Yeah. So ready, set, battle, snake was getting right after Oh, no. No. No. No. Hang on. Hang on. Go back. There's a there's a better value to put in here. Let's put in the game ID. Let's put in the because that's random. That's in every new game is a new is a new That's true. User in this context. So actually grab game. Okay. Double double, double brackets first, and it will be trigger dot body dot Game dot ID. Game dot ID because that's a random value each time. Yeah. It is. That's good. Yeah. Yeah. Yeah. That might that might do it. So we should now see 5050 if it works, which it may not. Mhmm. Mhmm. K? Good. So right on the first one, right on the second one. It's not looking good so far. It's not looking good so far, is it? Oh, there we go. Hey. Hey. There we go. So So that logic that I think that's Andrew, I I I'm I'm both really happy that we got this to work, But I'm also kind of sad because I think I think that might bring us. What do you think? I think I think we have we have eaten into to your evenings for many, many dates. Not for that reason. Not for that reason, but just I think that's I think anything more now is us just in hot like, doing things we've already done, but in this slightly altered context, but that's awesome. So you can really, like, yeah. So smooth. Yeah. I gotta say, like, I when we set out to do this, I knew that we might be able I knew Directus would work. Like, that made sense. But when we decided that I was gonna take the lead for the final show and we were gonna do the dev cycle, I was like, I don't know if this is gonna actually work because I tried to implement dev cycle with Directus a lot of ways. So I'm over the moon that this works. And, like, what a cool piece. And it's so fast too. Right? You're not having to worry about direct us doing anything. It's all managed externally. Like Yeah. And and part of this is an approach that we chose. And this is a very battle snake kind of thing. It is an approach we chose to make sure that we are we're not combating that 500 millisecond return require return requirement. And that is because we determined a strategy at the beginning of a game, not every move. So there is a couple more round trips within directors. We're having every move to go and figure out the game strategy, but this is all internal. These are just little database operations. That's awesome. And then, of course, going back to the last episode, we could now enrich our insights dashboards and saying when this was the strategy, this was the outcome. We could use the little global variable drop down to to pick. We could even go off to dev cycle, populate directors with all viable strategies, That was awesome. That was fun. Happy? This was a fun day. I'm I'm so Kevin, I was so it's been a couple episodes since I've been, like, on the keyboard. And, like, you were walking away so pumped after every and I was happy. I was definitely feeling it. But, like, I'm having a good this was a good It's good. This was a good day. We got we got things working, and our snake does the things, and other people can now go and use this to meld the world's all the d's, Directus, Devcycle. Yeah. What what do you say, as for as for Directus as a Battlesnake engine? I love it. I think it's super, super smooth. And I love the fact that there's actually, some code that exists out there for building a dev cycle management setup within the Directus platform. So, like, like, use Directus and then implement dev cycle and then play Battlesnake. That's the real that's the real goal here. Do Everybody wins. Everybody wins. Everybody wins. Everybody wins. Everyone, thank you so much for joining us. Andrew, thank you for getting on board with this idea to to, you know, have a Battlesnake video, you know, series after after a little while. Thank you audience for joining us for this limited series of Ready Set Battlesnake. If you want to learn more about Battlesnake as with all of these episodes down in the resources, you'll find a link to the docs for Battlesnake. It is such a satisfying and fun hobby project for developers at all stages of their career, whether you're just getting started or you are really pushing, you know, your very, very advanced knowledge and it becomes an optimization game. There's something in it for everyone. I'm glad they all worked. Honestly, there were so many points in this where we might have discovered this doesn't work irreparably and the whole thing's ruined. And every barrier we came against, there was a solution primarily actually through the ability to build extensions. Yeah. So that is wonderful. Oh, lovely. And a melding of my world as a big time and long time battlesnake lover and running dev rel, at, director. So Andrew, once again, thank you so much. You're welcome. And should we do it our one last try? This is our last opportunity? So here we go. What do you mean? What do you mean? You have little faith? This is it. You have a So here we go. What do you mean? What do you mean? You have little faith. This is it. You ready? Alright. Here we go. Alright. Do you want to count it down? Should we count it down first? No. Hang on. Hang on. Hang on. Hang on. I've got it. Uh-uh. Alright. This has been ready. Ready. Set. Set. Battlesnake. Battlesnake. Bye for now. Bye.",[224,225],"d2934fc9-1a8a-4260-8128-0ea918de1854","95293c50-dd25-47f3-b558-913cbe79f9b4",[],{"id":134,"number":135,"show":122,"year":136,"episodes":228},[138,139,140,141],{"reps":230},[231,287],{"name":232,"sdr":8,"link":233,"countries":234,"states":236},"John Daniels","https://meet.directus.io/meetings/john2144/john-contact-form-meeting",[235],"United States",[237,238,239,240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255,256,257,258,259,260,261,262,263,264,265,266,267,268,269,270,271,272,273,274,275,276,277,278,279,280,281,282,283,284,285,286],"Michigan","Indiana","Ohio","West Virginia","Kentucky","Virginia","Tennessee","North Carolina","South Carolina","Georgia","Florida","Alabama","Mississippi","New York","MI","IN","OH","WV","KY","VA","TN","NC","SC","GA","FL","AL","MS","NY","Connecticut","CT","Delaware","DE","Maine","ME","Maryland","MD","Massachusetts","MA","New Hampshire","NH","New Jersey","NJ","Pennsylvania","PA","Rhode Island","RI","Vermont","VT","Washington DC","DC",{"name":288,"link":289,"countries":290},"Michelle Riber","https://meetings.hubspot.com/mriber",[291,292,293,294,295,296,297,298,299,300,301,302,303,304,305,306,307,308,309,310,311,312,313,314,315,316,317,318,319,320,321,322,323,324,325,326,327,328,329,330,331,332,333,334,335,336,337,338,339,340,341,342,343,344,345,346,347,348,349,350,351,352,353,354,355,356,357,358,359,360,361,362,363,364,365,366,367,368,369,370,371,372,373,374,375,376,377,378,379,380,381,382,383,384,385,386,387,388,389,390,391,392,393,394,395,396,397,398,399,400,401,402,403,404,405,406,407,408,409,410,411,412,413,414,415,416,417,418,419,420,421,422,423,424,425,426,427,428,429,430,431,432,433,434,435,436,437,438,439,440,441,442,443,444,445,446,447,448,449,450,451,452,453,454,455,456,457,458,459,460,461,462,463,464,465,466,467,468,469,470,471,472,473,474,475,476,477,478,268,479,480],"Albania","ALB","Algeria","DZA","Andorra","AND","Angola","AGO","Austria","AUT","Belgium","BEL","Benin","BEN","Bosnia and Herzegovina","BIH","Botswana","BWA","Bulgaria","BGR","Burkina Faso","BFA","Burundi","BDI","Cameroon","CMR","Cape Verde","CPV","Central African Republic","CAF","Chad","TCD","Comoros","COM","Côte d'Ivoire","CIV","Croatia","HRV","Czech Republic","CZE","Democratic Republic of Congo","COD","Denmark","DNK","Djibouti","DJI","Egypt","EGY","Equatorial Guinea","GNQ","Eritrea","ERI","Estonia","EST","Eswatini","SWZ","Ethiopia","ETH","Finland","FIN","France","FRA","Gabon","GAB","Gambia","GMB","Ghana","GHA","Greece","GRC","Guinea","GIN","Guinea-Bissau","GNB","Hungary","HUN","Iceland","ISL","Ireland","IRL","Italy","ITA","Kenya","KEN","Latvia","LVA","Lesotho","LSO","Liberia","LBR","Libya","LBY","Liechtenstein","LIE","Lithuania","LTU","Luxembourg","LUX","Madagascar","MDG","Malawi","MWI","Mali","MLI","Malta","MLT","Mauritania","MRT","Mauritius","MUS","Moldova","MDA","Monaco","MCO","Montenegro","MNE","Morocco","MAR","Mozambique","MOZ","Namibia","NAM","Niger","NER","Nigeria","NGA","North Macedonia","MKD","Norway","NOR","Poland","POL","Portugal","PRT","Republic of Congo","COG","Romania","ROU","Rwanda","RWA","San Marino","SMR","São Tomé and Príncipe","STP","Senegal","SEN","Serbia","SRB","Seychelles","SYC","Sierra Leone","SLE","Slovakia","SVK","Slovenia","SVN","Somalia","SOM","South Africa","ZAF","South Sudan","SSD","Spain","ESP","Sudan","SDN","Sweden","SWE","Tanzania","TZA","Togo","TGO","Tunisia","TUN","Uganda","UGA","United Kingdom","GBR","Vatican City","VAT","Zambia","ZMB","Zimbabwe","ZWE","UK","Germany","Netherlands","Switzerland","CH","NL",1773850418674]