SwiftUI Tic Tac Toe | Multiple AI Difficulties | MVVM | Portfolio Project

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
today we're going to build a single-player tic-tac-toe game with variable ai difficulty meaning we're going to have like a super easy mode and then we're going to slowly scale up that difficulty through some logic this is the first video in a series i'm starting uh that's all about helping you create projects for your portfolio and that brings me to today's sponsor squarespace head to squarespace.com sean allen if you want to get your ios developer portfolio or maybe a website for your app up and running very quickly all right let's dive into the project so there is no starter project to review or anything i just did file new swift ui project here we are blank canvas a quick note though this is not for absolute beginners this does assume that you have some basic swift and swift ui knowledge so i'm not going to explain all the little basics but i will be explaining what i do of course but uh just to let you know this is more of an intermediate type tutorial and how we're going to build this of course we're going to start off building the game board ui then we're going to work on our game logic right with that ai difficulty and how do you make the the computer take a turn by itself that kind of thing and then at the end we're going to refactor clean up the code and refactor into mvvm model view view model so i think you're going to learn a lot in this video there's a lot packed in here it's not just a simple tic-tac-toe ui there's a lot going on so stick around i think you're gonna learn a ton so to kick things off let's build our game board that you see here on the design now what that is is that's a grid for us in swift ui specifically a lazy v grid and we're gonna have three columns right three columns and three rows typical tic tac toe board so let's create that here in our code let's get rid of text hello world let's do lazy v grid and we'll initialize that see it takes columns and content i'm actually going to get rid of this content parameter name just to clean it up we don't need it and then you see it takes a grid item right now there's only one grid item with a fixed width of two we want three grid items to make up our columns right we want three columns so instead of like putting it here in the initializer i'm gonna refactor it out a little bit to clean it up do let columns which is an array of grid items and that is going to equal my empty array here and i'll hit return here just to get rid of that placeholder stuff and essentially we're going to do something similar here so i'll command x this here command v it into my array now we don't want fixed we want uh dot flexible not flex and we're not going to do the autocomplete because we don't want the minimum maximum and again we want to have three of these so i'll copy hit return paste paste there we go that's our array of grid items right so now here in columns instead of like putting all that in there i can just put columns right so now we have our lazy v grid of columns we just have text placeholder that's what it gave us when we did the autocomplete but we'll go ahead and just for show here we'll do three of them here hit try again on our preview and you should see uh three i don't need that parentheses anymore that i got rid of the uh content parameter and now you see once i restarted my preview we got three uh columns of placeholder there's only one right if i had nine of them you would see nine actually let's do that just for illustrative purposes here real quick copy paste to get nine now you see kind of like the beginnings of our tic-tac-toe board except instead of the word placeholder we want to have some kind of like square or something that we can actually put like our x's and o's for tic-tac-toe just to give you the idea of like what we're doing here so instead of hard-coding nine things for uh each and we'll initialize that we'll just do id and content even though we don't need a lot of this so our data is gonna be zero up into but not including uh nine and i can actually get rid of a lot of these parameters here we don't need the id in this particular case and i don't need that and i can just do uh open and close view builder but i do need uh i in right because as we iterate through zero through nine right we want i for like our index right we want to be at place zero place one place two place three right that's how we know where we are on the board so uh what is in our four each right before we had nine placeholders well now i wanna have a z stack because our square or in my case is going to be a circle just for design purposes but we want to have whatever square we have there and then when the player taps on it we want to put an x or a circle on top of that so that's why we do the z stack right vertical views here so z stack and uh at the bottom of the z stack i'm gonna have a circle and i'm gonna do uh foreground color dot red and we're gonna do dot opacity 0.5 and by the way this is just my design choice the color the opacity the circle make this your own that's another thing i should point out for like 10 seconds like when i say i'm going to help you make portfolio projects don't just like copy and paste what i have take the time add some design to it maybe add a couple little extra features like make it your own before you add it to your portfolio just use this as like the launching point or like a base okay so now my circles are pretty small as you can see that's not what we want there so we need to give it uh some sort of frame but if i hard code the frame like 90 by 90 or something like that it's not going to look you know the same on all screen sizes so in order to kind of get the relative screen size i'm going to use what's called a geometry reader in swift ui so we're going to do that at the kind of the top level of the view so geometry reader here as you can see and we'll open up our view builder and do geometry in right because that's what uh that's what we're going to use to get our relative view size so we'll cut our initial v grid and put it inside our geometry reader that we have access again to the relative screen size now you'll notice it pushed up our circles to the top we don't want to do that so in our geometry reader here we're going to add a v stack with a spacer at the top and a spacer at the bottom this is going to put our lazy v grid here our tic-tac-toe board essentially command x put those in between the spacers that is going to put again the spacer at the top spacer at the bottom which will move our tic-tac-toe board doesn't really look like a tic-tac-toe board quite yet but it'll put that in the middle um so now back to our circle here and by the way at the end of this video we're going to spend a good 10 minutes refactoring so if it looks a little messy right now and confusing it'll look nice and neat by the time we're done but it's a process okay so uh foreground color uh dot red now we're gonna do dot frame and the width and height here so you see here we get these hundred by hundreds uh circles by default which kind of looks like we what we want but you can see the spacing isn't consistent you know both vertically and horizontally and again as the screen you know gets bigger or smaller on like a smaller iphone se or something like that uh it's going to it's going to change so that's why we want to use the geometry reader so we want to use geometry dot size dot width and then you're going to think the width should be divided by three right we want to have three equal squares and that'll work and then we can also do command c and command v into height to get that and i'm going to put my parameters here on two different lines hit try again oops i i copied the parameter name there let's not do that that's not going to work okay so this looks kind of cramped as you can see there's not like a lot of spacing like in between and especially if you had instead of a circle let's say you did rectangle you can do this right see how easy it is to change now you can see if i get rid of kind of like my red outlines it's it's hugging the side the spacing's a little weird it doesn't quite look right so we're going to actually put some spacing in here negative 15 to let it breathe or minus 15 i should say and then minus 15 again to let that breathe a little bit and i can see starting to look a little more symmetrical we can also let's go back to circle here but again if you want rectangle keep it rectangle that's cool we can also go up to our v grid i deleted this parameter before here on line 19 and i can add the spacing parameter back in and let's give it a five and there that looks a little more symmetrical you can like kind of dial that in and tweak it if you like but we're gonna stick with this for now and actually one last thing because it is kind of hugging the edges on our v stack here uh you can see it's highlighting that my v stack curly brace up there we are gonna just add some default padding again just to move it in from the edges a little bit so there we go that's the basics of our tic-tac-toe board now in the z-stack here right that just has our circle at the bottom on top of that we want to have the ability to either add like an x or an o so for that we're going to do an image in our z stack on top of our circle image scroll up a little bit here and we're going to do system name that's going to be way down here the reason we're doing system name is because we're going to use sf symbols and i wanted to do this to give you some flexibility for example i know there's a symbol for x mark or for the x it's just called x mark as you can see now we've got little little x's in there not quite how we want it to look so let's do something real quick so with image let's give it a frame uh we're gonna do 40 by 40. it's pretty uh universal image there or size there actually i forgot to do uh dot oops dot resizable so that can be big there you go nice big and bold that's what we're looking for and then the last thing is dot foreground color dot white i mean again change the colors make it your own that's fine but that's what we're working with here there's our our x's and for our circles instead of x marks it's just a circle as you can see here right and again so i just did uh sf symbols if you're not familiar with sf symbols uh it's an app with a bunch of icons here i'm gonna i have it open here that you can see that sf symbols a bunch of icons that you can choose from so you can imagine like i'll just go to like nature i mean you could do tortoises versus hairs right again you know ants versus lightning bolts whatever that doesn't have to be x's and circles but you would just copy this string for whatever you wanted to do and again there's like a whole bunch of them you can choose from yeah make it your own have a little fun with it i'm going to stick with x's and o's for right now though so that's the basics of our ui of course we've got a long way to go what i want to set up now is like our moves right we need to track the moves when a player taps on one of these circles we need to keep track of like what move i as the player made what move the computer ai made right we need to track all that so i'm going to create some objects here but let me get rid of this space it's really bothering me okay so down here above our preview i'm going to create uh some new objects we're going to create an enum called player and that is gonna have two cases uh human and computer right and you'll see throughout this project we're gonna be using that to obviously determine hey is this a human player or is this a computer player just a nice nice clean way to do that and another object we're going to create is a struct called move and that is going to have a property called player which is of type player right because either going to be human or computer it's also going to have another property let board index right again this is going to be an int so the definition of a move is you know what player made the move where on the board was it and then we're going to have a an indicator here a computed property that we can figure out it should be an x or an o or whatever indicator you chose right so we're going to say var indicator which is a string because it's the system name of the sf image we're using and we're going to basically return if the player we're going to use turn memory operator here player equals equals dot human if that is true we're going to use the x mark and if that is false which means it's the computer we're going to use the circle and again this is where you can put in whatever you like here but that is our our move object again the definition of the move a move has a player a move has a position on the board and then a move has an indicator so we're going to basically keep track of these moves in an array and that's how we're going to build up our game okay let's scroll back up to our our uh view here our content view uh we'll rename that later but let's create a state property because this is going to be the array of our move so we're constantly going to be changing this array so it needs to be an at uh whoops i'm in the wrong spot here i even put this array or this variable in the wrong spot my bad we're going to refactor it later anyway that's not going to end up in the content view at the end of things but my bad on that still work but if it's outside the struct in case you didn't know what happened there that means it's like a global variable that your entire app has access to and that's that's kind of dangerous we want to keep that within the content view so my little mistake sorry for any confusion but now we're going to do an at a state private var called moves and that is going to be an array of type move and it's going to be the move is going to be optional right because we're basically going to create an array that holds 9 items and if there's a move at that position we're going to have our move object and if there's not a move yet at that position we're gonna have a nil so you can kind of see when a player tries to tap on a circle we're first gonna make sure that like there's not a move already there right so the basically the object at that index in the array has to be nil before we allow a player to make that move yo we're going to code that up later but just so you know why i'm creating an array full of nine nils to start off so that's going to equal we're going to create an array and initialize that and you can see we get this repeating autocomplete so we want that to repeat uh i'm sorry we want to repeat nil that's what we want to repeat and then the count is nine so again create an array of an optional type move um with nine items um nine nils essentially that's our empty game board is what that is all right so the foundation's starting to come together now let's implement the ability to you know tap on one of these spaces and have our indicator show up uh so for temporary reasons we're not gonna this is only for like testing let's create another at state uh private var called is humans turn and it's just going to be we're going to say equals true to start right this is just going to be what we're going to use to kind of flip back and forth between the x's and the circles while we test out our ui so what i want to do here is on our our four each right this because this is the item that's in our grid so this is each one of these little red circles uh is basically the z stack in the four each so on the z stack there not the four each itself i want to add a tap gesture right so each one of our red circles is going to have a tap gesture and this is where we're going to kind of like work our magic you know when a player tries to tap it to put their x there you know we're going to do all the all the game logic stuff okay so the super simple version of this this is going to evolve as we go again you kind of got to take baby steps when you're building a program you don't just kind of like have the end vision in your head and build that so uh the baby step that we're going to kind of make here is that on the tap gesture we want to basically add a create a move and add it to our move array right so we access our moves array right and then at object or i'm sorry at index i and what that means is if you're not familiar right in our for each right we we basically called each one of our circles i which is you know zero through nine so if i tap the middle circle that's going to be index four right zero one two three four so that's kind of what this means so if i tap the middle one basically at index four add this move is what i'm trying to say here so the the new move equals uh we're going to initialize a move and the player is going to be uh that's why we created this is human's turn so if uh it is is human's turn again another ternary ternary operator here if ishuman's turn is true the move uh the player is going to be a dot human else it's going to be dot computer right now you're starting to see that enum come into play and then the board index that we create is i right cause it's whatever one we tapped so again we created our move our move object here no now we know the player we know the board index and the indicator gets kind of figured out on its own so we created our move based on a tap and then what we want to do after this again is kind of temporary code but now we want to do is humans turn dot toggle right so that means now it's the computer's turn so the next time something gets tapped you know now this will flip to computer and we'll add a circle instead of an x of course eventually the computer is going to be making its own moves and we're not going to be like doing this manually but again we're just trying to get our ui working all right so now i need to change my image system name up here so it's not all x's right i want to be able to i want to start blank and then be able to tap and get x's and o's so the system name we want is moves at index i so remember this is basically our array of moves uh if there is a let's do well let me finish typing before i explain that'll help out dot indicator or blank so basically what i'm saying here is if there's a move at index i right because it could be nil right we're starting off with the whole array of nils um then we use the indicator uh this is called no coalescing to unwrap an optional so if the if it's nil at that index then we're going to use blank right because if it's nil there's there should be no xro in there okay so now as we're creating our moves on tap you know we're going to put an x we're going to create a move with an x or a circle based on human or computer right based on whose turn it is and i should be able to tap and put xs and o's on here uh it'll just be the beginnings of things because it won't quite work how we want but you'll see by the way i'm in dark mode as you'll see here if you do command shift a you can switch between light mode and dark mode real quick on your simulator anyway so let's tap lower left okay there's my x and then remember we switched is human's turn toggle that to so i should see a circle there we go x circle x circle now of course we need to get this to where the computer is making their own moves and doing it on its own but again we're just testing out the ui and we have no win conditions yet we got to check for all that like circle should have won but here's what's wrong with our ui because every time we tap we're adding to the or to the moves array well what i can do here is like oh i can i can take circle you know no let me take over this right i can overwrite on uh you know the thing if if something already exists right so we need to have a check to make sure there's not already a move at that index in the array right so basically what we're going to say is hey if the space is available go ahead and put your your player's mark in there uh if the space is not available basically don't do anything and like make the player you know tap something so that's what we're going to do right now so let me uh stop my simulator here and you know still in our content view right below our body we're going to create a function and quick note all these functions eventually are going to get refactored into our view model in model view view model for now we're putting them all in the view but just note with swift ui your your logic and your functions should be on your view model we'll get there at the end of the video so uh funk called is square ocu pied and then we're going to say in moves because we have to pass an array to check right so we're going to pass in an array of move and it's going to have a nil right because there could be some nils in there and then for index we'll say index and then int and this is going to return a boolean right because basically true or false is the square occupied true or false yes or no and the quick check for that is let me scroll up a little bit uh return uh moves dot contains we're gonna do dot contains where and again we're gonna do a little little advanced swift not like crazy advanced but you know more than the basics here and we need to pass in a closure here so dollar sign zero uh dot board index equals equals uh index now if you're not familiar with this line of code let me kind of walk you through it so basically i am going through my moves array that i pass in so we're going to pass in again this this array that we're kind of holding all of our moves and we're going to check it so what we're going to do is we're going to look to see if this dollar sign 0 means for each individual element in the array so for all the moves or the nils in the array well if it's nil this optional just won't even trigger so it's really only going to check the actual moves in the array if that move dot board index equals equals the index that we pass in so we're going to pass in a specific index that we want to check if that does contain it then return true that's an occupied square if you know this never happens then it's not occupied it is free to go so hopefully that made sense you'll see how we'll use it here so in the ontap gesture we can use it like if is uh square occupied in again we're going to pass in moves and for index i because it's whatever one we tapped remember our tap gesture is on the z stack and each z stack is each individual square so basically well if that's true we want to return so essentially what we're saying here this is kind of like a a line in the sand if the square is occupied in our moves array for the index that we tapped just return don't execute any code below this essentially nothing is going to happen so let's actually test that out here on our iphone 12 pro here so you can see i'm going to go middle square and x now before when i would tap and i get a circle and then x so i should get a circle here but now when i'm tapping middle square nothing happens now when i'm tapping this square nothing happens because we're checking that to make sure it's occupied it's an occupied square you can't do anything now this one's not occupied cool it's there okay so our check to see if a square is occupied is working next we're going to write the code to have our computer player actually make the move itself so i don't have to tap for x's and circles and then we're going to create our very easy ai which is super basic which is essentially just pick a random square that's not occupied right no no logic in tic-tac-toe just any occupied square put it at random that's our very easy ai so let's start doing that so for that we're going to create another function below is square occupied and again all these functions are going to get refactored out into the viewmodel but the func is going to be called determine computer move position and we want to say in move so we have to like determine it within this moves because again we got to check to see if it's available which is an array of moves and we're going to return an int right because we want to return say hey once we've figured out what position we want here's the position to put the circle in right this is kind of the computer playing itself so first let's create a a variable called move position and that is going to equal just a random integer again int dot random and we're going to do 0 up and 2 but not including 9. that will give us 0 through 8. that's what our grid maps out to so here's us just hey a random position but once we have that position we have to make sure it's not occupied right we can't just occupy something there's already an x at so we're going to use the handy dandy function we just wrote up here on line 50. we're going to say if is square occupied right in moves right this whole moves array is basically our whole game uh for index move position right because we want to basically check against this random position right we're going to basically roll the dice a nine sided dice uh if we get eight okay let's check to make sure eight is not occupied so if if it is occupied so if this returns true uh we're gonna do the same thing again here we're going to basically command c and command v uh update this but this won't quite work right because this if statement's only going to run once so we want to create a while loop here so say while is square occupied so basically what's going to happen is this is going to run and if the square is occupied it's going to basically change the move position to another random number it's gonna run it again and then as soon as this equates to false then it will exit the while loop and continue and once it does that means we have a good move position so we can return move position right so again to really sum this up we're just getting a random number checking to make sure that we're we're not on an occupied square and then if we're not on an occupied square uh return that move position which is an integer um which is what we're going to say hey computer move to this integer essentially what we're trying to figure out right here now this function this looks short and easy this is going to get uh there's going to be a lot going on here because this is where we're going to build out our ai to be a lot more difficult right we want to check to see if they can win and if they can win they make that move then we want to check to see if they can block and if they can block okay make the blocking move and then you know if they can't win they can't block okay now take the middle square and if they can't win they can't block they can't take the middle square now do a random one so that's we'll get more into that later but that's the overview of like rai and we're going to code all that up so now that we know how to determine just our basic uh ai's position which is just a random square that's not occupied in our tap gesture how we're going to do this here okay so we no longer need uh the if is human term dot toggle get rid of uh that um yep it's human's turn got get rid of that you're gonna see because we're gonna create another one of these for uh computers but i wanna make sure i don't forget to get rid of that and yep get rid of this uh is humans equals true state uh property again that was just temporary to test it to show you the ui so now what happens on tap right and and again this is gonna get refactored as well because we're gonna have a lot initially in this ontap gesture but uh now that we have our human move right if the human was able to make a successful move well here's where later on we'll do our whole you know check for win condition uh or draw right because you can draw tic-tac-toe if you have two good players you're probably going to draw all the time so we're going to do that later but first let's kind of get our computer making their own move so the behavior we want as soon as a player taps one of our circles there's going to be like a half second delay and then the computer is going to make a move now the reason i chose to do that was instead of doing it instantaneously that's kind of confusing for the user imagine if like the instant because it's a computer right the instant you tapped the button the other circle showed up and it would just kind of look weird so i put a half second delay in there just to make it look like the other player was like making a move um that's just kind of like a ui ux type thing i chose to do dispatch q dot main dot async after and then uh yeah deadline is dot now and then uh plus this is where we're putting in our delay right 0.5 so that's half a second uh delay here and then we don't need that we can just do that and then put our closure there so now this is where after 0.5 seconds we're going to have our computer make a move now hopefully if you're a little bit experienced you may notice that you know by delaying half a second well what if the user just immediately taps another circle you're kind of breaking things we're going to address that but let's get our computer making its move first so basically after half a second from our player makes the move we want to essentially do the same thing except for the computer player so here we're going to say let computer position uh equals determine this we're going to use our function that we wrote down here right we wrote this determine computer move position right now it's just a random number but again that that function is going to get a little more uh evolved here in we've got to pass in our moves there we go so that is going to spit out a random in that's not occupied right all that work is happening down here in this function so once we have our computer position we want to do the same thing we did up here for the human in fact i'm going to copy and paste this just to make sure you don't mess it up so command c i forgot to have the parentheses at the end there and then down here uh command v so see moves uh at i which again is not what we want right because we don't want to put it wherever i is whatever we tapped but here we want uh computer position right because the computer determined its own position right it got that random number so at the index you know so the computer said i want to go to index nine or eight whatever that's what we're putting it here and then the move is not a human move it is a computer move and the board index again is a computer uh position i'm going to make my thing smaller there so we can see this better okay so again the behavior here is once we tap a button we're going to put our move in as long as the square isn't occupied then we're going to wait half a second and then the computer is going to determine their random move and then it's going to be my turn again that's going to be the flow of things that you'll see here so let's run this and try again we will handle the uh the player like double tapping uh real quick but we're gonna we're gonna play fair and just wait okay i'm gonna try to go to the upper right there's my x where's the circle okay let's check our uh determine computer move uh position okay i should have looked at the warnings duh that was super obvious so small mistake i made right so you can see it says variable move position was never mutated right well of course we want to mutate that move position so i accidentally created a brand new variable here so this isn't working correctly i'm picking a random number i'm creating a brand new move position right so that's where that error was because what i want to do in this while loop is update the existing move position that's how that's how while loops work uh so anyway that var right there is messed up make sure you delete that now we should be uh working right because i want to keep continue updating you know this move position on row 59 not creating you know a new one every time okay so put the x in the upper left circle there uh we'll put the we're going to try to let this thing beat me put the x here circle there x here i guess not okay so you see we're playing some tic-tac-toe uh there's no wind condition normally you should see an alert here uh we got to check for all the win conditions and stuff but you can see our computer is and i have to like rerun the thing to restart we'll implement a whole restart game thing but you can see circle's making its own moves uh just picking a random uh spot so pretty cool we're starting to get there with our game now let's handle the issue of you know if i were to like double tap i would mess everything up because there is that half second delay here that you know we're just kind of like pausing well what i want to do is basically as soon as i tap the button as the human i want to disable the board so on my v stack right which is basically like the whole screen here uh we got this right here right where the padding is i want to do uh dot disabled and i need to pass in a boolean here well let's go up here make a state variable because we're going to be flipping this variable back and forth at state private var is game board disabled that is going to equal false to start and then basically as soon as i tap it i want to set is game board disabled equal to true and then after the so for 0.5 seconds it's going to be disabled that means the player can't tap it and then as soon as the player makes their i'm sorry the computer makes their move i want to do is game board disabled equals false so we're flipping it back and forth uh and then in disabled here instead of just true i want it to be is game board uh disabled so if i test that real quick i should i'm going to try to do a quick little double tap and it shouldn't work the game board should be disabled so xx yep didn't work xx nope i'm trying to click all around super fast it's not working so okay so we took care of that little little potential bug and that's something you got to look out for when you're putting in like delays like that because you know user could quickly double tap so we handled the game board disabled we have our computer playing very dummy a dumb game it's a dumb ai but we're going to make that a better ai but first let me talk about today's sponsor today's video is sponsored by squarespace and that's great because the whole purpose of this video is to help you build a portfolio project so squarespace is a great way to get that ios developer portfolio up and running very quickly now i know as a developer you may want to try to build it yourself we all have that inclination but it's a lot of work to build and maintain your own website you have you know mobile ipads the giant imac that i'm looking at right now all the various screen sizes are a pain in the butt to implement not to mention all the different browser compatibilities it's just it's a headache and a lot of time to maintain and build your own website so that's why i say let squarespace take that off your plate let them do it for you they have tons of beautiful themes they do all the seo and the analytics again it's just a stress that you don't have to deal with with your ios developer portfolio or even like a website for your app so when you're ready to build that website go to squarespace.com and when you're ready to launch go to squarespace.com sean allen to get 10 off your first purchase of a website or domain okay so we have our dumb ai playing the game before we make that ai pretty smart and pretty good at tic-tac-toe let's actually put in these win conditions right because as you saw every time i played a game i have to like stop and rerun the simulator all that stuff so we're gonna actually check for wins and then put in the ability to reset the game that way we have like a working tic-tac-toe with a pretty stupid ai and then all we have to do is to finish up the project is you know work on uh making our ai smarter so for that we need to write another function uh so you can see there's there's a lot of functions going on here which again is why we're going to refactor that so under determine computer move position we'll do uh funk check win uh condition and then we'll do four and we'll pass in a player which is of type player right so we got to see if the human one or the computer one and then in uh moves which again is our array of move which is optional because again it can have nils again this array of moves is essentially our a representation of our game board and that returns a boolean uh because we're gonna see basically return we're gonna hit return true just so uh xcode doesn't yell at me before we write our code in and now for the sake of time i'm going to copy and paste in some quick win condition code i'll explain it it's not a lot of code i just don't think you want to watch me type out the tediousness of it before i do that i forgot to actually return the boolean um i'm not sure what happened there but now if i do command b all errors should be gone good to go okay so we i want to bring in the win conditions right i'm going to paste this here like i said i brought this in but i'll explain it so i call this win patterns right and it is a set of sets so a set is similar to an array except it's unordered uh you can have direct lookup it's much more efficient when trying to do what we're doing even though this is a small data set it probably wouldn't matter but anyway so a set of a set like you see it's it's i'm just going to call it an array for ease of explanation so it's like if it's like an array and then you have arrays within an array right so an array of arrays and these are the win conditions um right so in order to win you have to be at 0 1 2 right so 0 1 2 would be across the top you see over here 0 1 2 and then 3 4 5. that would be across the middle 6 7 8 across the bottom etc and then all the diagonals so these are all the basically the series of board indexes that will create a win is what this is right so this is every single win condition there's only eight of them uh but that's what this is right so to explain that feel free to pause the video type that out uh i feel like this video is going to be long enough so i wanted to kind of cut time there okay so what i want to do to check against these win conditions is first i got to start pulling out a certain player's moves from our moves array that we passed in again the representation of our game board so i'll say let player moves equal moves and the first thing we got to do though and again you may not be familiar with like map compact map filter you're going to get a little exposure here moves dot compact map and then again we're going to pass in a closure here and it's going to be dollar sign 0 for each ind you know item in there and i can delete that actually and basically what pla this does is compact map removes all the nils again our moves array can have moves or nil well this basically gets rid of all the nils and just give me the moves okay so now when player moves all i have is basically all the moves without the nils so now after that i can do a filter i can kind of chain these together dot filter and again another uh closure like we have before and then now with within the not all the nils removed i want to do dollar sign zero dot player uh is equal to whatever player we passed in i gotta type better right so the player we passed in is is right here so if we pass in a human what this line of code is doing is it is removing all the nils from moves and then we're filtering out all the human right so give me all the non-nil human moves and if i pass in computer give me all the computer moves right so now once i have all these moves like isolated now i can check them against all these wind conditions but there's actually one more step because i have the moves right i can't check a move like because move is a whole object that is a player an indicator all that stuff against just an integer so i need to yank out just my board index on the move right so that way i can compare integer to integer so we're kind of just parsing these arrays real quick so now i want to say let player positions equal we're going to create a new set here because we're going to use that later player moves basically this online 76 what i just created my filtered thing dot map and again another filter another closure i'm sorry you're getting some exposure to the filter map and all that good stuff uh so what i want to do is give me all the dollar sign zero which again is every element in the uh array uh board index so let me review this because i know a lot of people aren't familiar with with these uh this kind of code here so what i am essentially doing is again player moves is removing all the nils and then filtering out whatever player i passed in and then this player positions set is basically okay go through all the player moves and now just give me all the board indexes right so now player positions is just it'll look like this it'll be like 0 1 8 right it's where all the x's would be in the case of a human where all the circles would be right but it's just a set of integers and the reason we're doing a set is because that's how we're going to compare against the wind patterns basically what we're going to say is hey go through all these different sets of wind patterns and if this wind pattern is a subset of my player moves basically that means hey if my set of player positions has a zero a one and a two in it hey that means i have a win if it has a one a four and a seven in it okay i have a win so that's how i'm checking all the win conditions so let's write the code for that for pattern in wind patterns again i'm iterating through all my wind patterns uh where pattern dot is sub oh no i'm not getting auto complete of uh player position i'm just going to try to brute force this i don't know why i'm not getting auto completely here maybe maybe you all are and then we'll do command v still getting in there well i have an extra p in there command b still getting in there no that worked okay not sure if you ran into autocomplete issues there like i did who knows with xcode sometimes but let me explain this line of code here right so we're iterating through our wind patterns and then during that iteration wherever the pattern which again a pattern is one of these right zero one two three four five six seven eight that's a pattern uh is a subset of my player positions so like i said before if my player positions were you know two four six eight then yeah it's a subset right so i win that's how we're checking the wins so if that is the case uh here we are i like to put this on one one uh one line if it's super simple return true so the win condition is true if uh you know the win condition pattern is a subset of my player positions and then basically if if it goes through that whole iteration and nothing happens then we return false there is no win condition okay that was a lot let me do one super fast run through right here's all our win patterns we are basically parsing out our array to just to get uh you know my board indexes for that player and then we are going to basically have this set of board indexes and we're going to check to see if any of these win conditions are a subset of my player positions if that's true we have a win if it's false we don't have a win and we're gonna move on so back to our kind of game here on the tap gesture right under our comment that says check for win condition or draw so let's say uh if check for win condition for human right because this is after our human taps it basically the human move is up here the computer move is in our dispatch main.async right so uh four dot human in moves and then if that is true uh for now we're just going to print human wins we're going to have alerts and all that stuff later but for now so let's copy this too because we have to check it after the computer moves right so let's check it here but except we want to check the win condition for the computer not the human and then we'll say computer wins great so let's run this and test this out i'll kind of move this uh i'll move this over here so you can see the print out okay i'm going to beat this real quick right x x okay see it printed human wins if the the game is still playing because we haven't like stopped it we'll do that later but you can see in the print out human wins um i don't i'm gonna try this once i'm gonna try to get this dumb ai to beat me it's pretty hard we'll see what if i can make it happen uh i'm going to try once okay there you go there's your there's your spot circle come on ah there you go see computer wins right i let it win so there you go our win conditions are working okay let's stop this and now let's do our draw conditions that one's actually kind of easy here so under check when condition let's create funk check for draw in moves and again we're going to pass in that array of uh move and again just like the win condition we are returning a boolean and this one's kind of easy one line of code so what we want to do is uh return moves right dot compact map remember so let me tell you the logic before we write this code i don't wanna i got ahead of myself essentially what we're gonna do is we're gonna run compact map to remove all the nils right remember our array is just nine objects uh it can be nine nils it can be a mix of moves and nils so i want to do compact map to remove all the nils and after i do that if the count of that is nine that means i have nine moves so that means the game's over and there's a draw right because we're going to check for the draw after we check for win conditions right so if we check for win condition there's no win condition but all nine moves have been made that's a draw so let me write that out again so return dot moves dot compact map and i don't do the autocomplete again just kind of do the the closure here uh dollar sign zero space that out a little bit dot count uh equals equals nine so basically if i remove all the nils and the count is equal to nine it's going to return true because that's what this equates to uh if it doesn't return nine it's going to return false that means there was no draw so again the check for draw code uh pretty simple uh when you write it using you know compact map and all that stuff so now let's check for draws up here in our uh tap gesture after our um human goes so if check for draw in moves is true print you know draw and the same check goes down here after the computer makes their move and again we're so here's what if you're hopefully alarm bells are kind of going off thinking man this is this is like you know 40 whatever lines of code in a tap gesture on swift ui i get that we're going to refactor it out you definitely don't want to have all this kind of logic because it's confusing well it's not even let's not this confusing so we're going to refactor that and fix it we're kind of spelling it all out for now so again uh let's try to get a draw here uh i'm only going to try this once we'll we'll test the draw thing on a when we get the ai harder because it's easier to draw i don't know if i can get this random ai to draw i guess i can just yeah i can i can just sit here and block them all day and not try to win crap um okay it's hard to try to lose tic-tac-toe okay now that's a win damn it oh but okay so draw popped up because we're not like i gotta return after this that's what i forgot so after i print win i have to return so nothing runs after i check the win condition or if the win condition is true i return if the draw is true i return because we want that we want nothing to happen after that right uh i forgot that that's why the code kept running but uh anyway you'll see the draw code work once once the ai gets a little harder before we start making that ai a little harder let's actually put in our alerts so we can actually end the game and restart it you'll see that'll make the testing much more easier i don't have to stop the simulator at all times and all that good stuff so uh a little intro into like swift ui alerts we'll do command n on the tic-tac-toe folder new swift file we're going to kind of have a new alert object here we can call this alerts this is the way i do alerts in swift ui at least the the super basic and simple ones um obviously if they get more complex you may have to mix it up a little bit but so i'm going to import swift ui here create a new struct called alert item and it's going to conform to identifiable you'll see why we have to do that uh in a second or once we start out implementing these let me get rid of this and you need all the space i can get you know say it doesn't conform to it identifiable that's because we need to do let id equal uuid initialize that's create a new uid there all right i need some properties here so var title it's going to be a text and var message it's also going to be a text that i pass in var button titles also going to be a text that i pass in and then so you'll see how i use this in a second but let me create my uh struct called alert context you'll see how all this ties together and i'll come back and explain it if you haven't seen this before and here's where i will initialize these alert items and you'll see i'm going to have one for each case you'll see how this looks nice at the call site right it may seem like a lot of extra work right now but you'll see and i'll explain it all when i'm done here so i will say let human win tab over equals alert item which i did the object i just created up there see the title uh we'll say you win great uh the message is you are so smart you beat your own ai make to say whatever you want and then the button title uh will say hell yeah okay so that's that's one uh alert item there oh i'm sorry i just put the strings there i need to put the text here and i wanted to pass into text again this makes the call site look a lot neater and then speaking of neatness let's go ahead and i this is what i do when uh when all the parameters start to get like really really long uh then i i put them on the next line it's kind of like my rule of thumb it all depends on like how it looks so what i'm going to do to save time now is copy that do paste paste cool and then this is not human win this is computer win and then this one is you know draw and then i like you know i like to line my stuff up we're going to fix this all in a second uh i guess it already is fixed so now it's going to say you lost and it'll say you programmed a super ai and then the draw is you know i'll just say draw and we'll say you know what a battle of wits we have here and then uh you know we'll say try again for that button and then for the hue loss title that says uh rematch okay you can make this say whatever you want uh now let's let me show you how to use these right we did some heavy lifting here right kind of typed it all out here but this way we're not cluttering up our swift ui view that usually has a lot going on with all this like alert stuff i like to kind of refactor that out so back to our content view let's actually put these in action so on our v stack here like where we have disabled padding we can do dot alert and then you'll see here we'll type alert you see is presented or item you've probably seen the is presented where you have a state variable like is presented as true represented as false and that's how you trigger it well this one uses the item and you can see now uh identifiable that's why we conformed to identifiable so the item is actually we need to create a state variable for that so at state private var alert item that is of type alert item it's an optional and now we can do for the binding to dollar sign alert item that we just created then we'll pass in the closure alert item in and we'll space that out hit return and then in here is where i create my alert right this is where and this is where it can be dynamic right i'm gonna pass in whatever our alert item is so here i want to do uh title message dismiss button and then you see for title because i have access to the alert item which we're going to update you'll see here in a second i can do alert item dot title and like i said this is where i was talking about how it's neat at the call site i can do alert item dot message and then the dismiss button dot default and actually let me do this here to make this look neater here because it's going to get messy so i like to leave the button kind of flexible because alerts you know sometimes there's two buttons sometimes there's one button sometimes there's an action on the button sometimes there's not in this case there's going to be an action on the button because we want to reset the game once we hit that button so we'll do default with an action as you see there so the label text is going to be alert item dot button title so i put that there and then the action is going to be a function we haven't written yet called reset game so let's go down and write our reset game function down at the bottom below check for draw func reset game and this one is pretty simple so remember our blank game board was just nine nils in our moves array up here so essentially i kind of want to copy and paste and redo this whole thing i want to set moves equal to an empty array of nine nils and that kind of resets the game here so we'll do moves equals that oops i can hit spotlight do command b to make sure i got no errors okay so that's how we gonna reset our game is just basically put all nils back in our moves and then the action we're going to pass in the closure with reset game right there so pretty simple so let me kind of uh well actually we're not done with the alerts sorry so here instead of printing human wins right i'm going to set my alert item this is where it all comes together right so instead of printing that alert item equals alert uh context dots i forgot okay back to alerts i forgot to make these all uh static sorry about that sorry for the confusion there go back to your alerts make these uh static variables and uh you'd be good to go there and then i'm gonna highlight these all and do a control i to like line everything up a little little tip there if you weren't familiar with that okay so now i can do alert context dot there we go human wind right and then for the draw i can do alert item equals alert context dot draw and you see when you're putting in your so when you set it all up in that alert file when you actually want to use your alerts it's super simple and clean at the call site rather than having those alerts like rather than having like all this type of code sprinkled all throughout your view it just gets so cluttered and ugly in my opinion okay so for the computer wins alert item equals alert context dot computer win and then i'll just copy and paste the draw one down here okay so let me walk through how these alerts are working right so we have this state variable called alert item so anytime you know we get to this win condition i set the alert item equal to the alert context.humanwin which again back in our alerts is this this right here i basically set all that information and then because it's a state variable anytime alert item changes it kind of redraws the ui and then now that i have the alert on the vstack it is bound to that alert item so once that alert item changes it knows to show the alert and it has the alert item object and then i just populate my alert with all the different you know alert item properties this is how you can show like multiple alerts on one screen by kind of making it reusable and then all the alerts have the same action of resetting the game so now let's try this i'll try to win we should see the alert when i win and i should be able to reset the game when i say okay and we're still on the dummy ai so i shouldn't lose right yeah there we go you win you are so smart you beat your own ai hell yeah and when i hit hell yeah resets the game let's try to okay uh we didn't quite reset the game i think i messed something up here in uh where i'm doing my return after checking for win condition yeah that's what i'm doing so i'm i'm disabling the game board up here but then i check for my win conditions i won and i return so i never get the chance to turn the game board back on so what i need to do do a command x here don't disable the game board until after i do all my like win condition you know draw condition checks okay so let's test that out one more time here let's get a quick quick dub i should go in the middle first i don't know why i'm not doing it okay cool you win hell yeah okay go to the middle all right there we go now we're working boy this ai is not very smart okay cool so we're working there uh the app is working of course we got a lot of refactoring to do but let's make our ai smarter because as you see like it's not even a fun game like the ai just doesn't even try so let's fix that okay let's stop running this and let's focus on our uh determine computer move position function it's not a very big function right now we're about to add a lot to it okay so first let me put these comments above it right because this really breaks down what we're trying to do for the ai i said it real fast before i'll say it again so the first thing we want our ai to do is if you have the ability to win take the spot take the win if you can't win okay now check for if the x can win if the x can win okay now block then if you can't win you can't blot right it's like a series of instructions if you can't win if you can't block okay now take the middle square if it's open right that's strategically kind of like the next best thing you should do and then if you can't win you can't block you can't take the middle square okay now just take take a random square and this random square is what allows for mistakes because as you know two good players should nobody should ever win in tic-tac-toe it should just be a draw but to make the game fun and not make an unbeatable ai uh add that randomness in there so the ai can make mistakes on this final level which will make the ai smart but not unbeatable so let's actually take this piece by piece right i'm going to copy that first part here and we'll put it there and then actually put this because we already have like our last step that take a random position we already have that here with the last step so what we're going to do here is build these three steps here and you can see this is kind of like difficulty levels of an ai right if you just wanted to have hey if you can win take the win if not go random you know that's going to be a pretty easy ai so you can see every step that we add makes the ai more difficult so you can imagine you could really expand on this app to where you could allow the user hey select easy mode medium hard and you can you know adjust the ai accordingly okay so the first thing i'll need to check for like wins and blocks is kind of my win or my win patterns uh i'm sorry because we before we were checking to see if all three of them were included now we want to see if there's two out of three right because if you have two out of three that means there's a win available same thing for when blocking if my opponent has two out of three that means they have a win available so that means i need to block that so a little bit different than checking for the win condition uh but let's go ahead and do that okay we need this same code we wrote down here we're gonna again we're gonna refactor this later i'm kind of keeping this all spelled out for your ease of understanding in the beginning but i need this uh first here except i want to be specific not on like player i want uh i want to make sure no for this computer i want to see and then i'll even name this differently right uh computer moves and computer positions uh and we'll change this here i know i could have done like edit all in scope chill with the keyboard shortcuts but uh anyway so i want to get same thing we did down here and checking for win conditions basically i'm removing all the nils from from the moves i'm getting all the computer moves and then i am going through all the computer moves and just getting a set of the board index same thing we did for checking with the win condition we just got to write a little bit different code to check for two out of three rather than all three right we can't do is a subset because that includes all of them so same thing to start i want to do four pattern in win patterns right because i want to go through each one of these patterns and check to see if i have two out of three so as i'm iterating through this i can create a variable called uh win positions and that is an array that's going to equal pattern dot subtracting other sequence other set we're gonna subtract the set here of computer uh positions okay so let me explain what i'm doing here so for each pattern where again that's these one zero one two three four five uh i'm creating a array or another set i'm sorry of possible win positions right and what i'm doing is i'm taking this pattern and i'm subtracting my computer positions right so i have say the pattern is zero one two but in my computer positions i only have a zero so i'm subtracting that zero out from zero one two right anything that kind of like intersects so that means my win positions will be one and two well if there's two of them that means i don't have a chance at a win right i need to do this when the count is only one right if i do the subtracting and there's only one left in the win condition that means that's my move that's where i got to go to get the win if it's available so that's what this does this gives me a set of the position but it can be you know three of them two of them one of them so i want to check to make sure it's only one because that means i can win so if win positions uh dot count uh equals equals one and if the count is one well now i gotta make sure it's available right if it's not available i can't use it so let is available equal here's we're going to use our is square occupied in moves right we pass that in for index we want to check the win positions dot first right and we can force unwrap this because we know there's one because we have a count of one in there and we can do the first one because there's only one so now except this is going to tell me if it's occupied right is square occupied so i want to use the bang in front you know to be not right to flip that so if the square is not occupied now i have uh is available equals true right so this is going to be true this is a boolean so now i want to say if is available then i'm going to return and it's basically this win positions.first right whatever that position was if it's available that's what i want to return uh let's take it right and when we're returning here now this this whole function of determined computer move positions is returning right we found our position so anything below this isn't going to run right we're not going to find a random position because we've got our position so you're going to see me returning out of this function a couple different times based on if we found our position okay so let's test this out right this this dummy ai is getting a little smarter now if it has an opportunity to win it should take it right away okay so i'm going to take the worst squares or at least try to try to not affect their their win here okay they have a win condition right lower left so i'm going gonna pick this one they took the lower left you lost you programmed a super ai great that win condition is working i always like to test things twice just to make sure like you know i didn't get lucky uh let's see here okay the circle is their win condition see if they get it got the win condition we lost okay so that is working now the blocking is very similar right because remember it's just my my uh if i have two out of the three win conditions or i'm sorry not win conditions but win patterns so i'm going to essentially just copy and paste this code and again we're going to refactor later but it's essentially the same we just got to switch it out for the player so now it's under the uh if i can't win then block comment here so instead of computer moves call this human moves and command c and i'm just renaming this here and then instead of that computer i want dot human right we want to check the human moves and then instead of calling this computer positions call this human positions and again copy we're going to change this from computer positions to human basically anywhere you see computer change it to the human one so now the logic is the same i'm going through the array of moves uh getting rid of all the nils filtering out just for all give me all the human moves and then once i have all the human moves now i'm going to create a set of just their board indexes again so i get something similar to my win conditions that i can check against we go through those wind patterns and then basically like i said we're gonna subtract out uh human positions so one more example let's say uh my human position and i have like three four seven and eight on the board well when i subtract that out for this wind pattern i subtract out three and four now i only have a five so now my win positions dot count equals one so that means i there's a availability to win uh so again we said make sure that square is available let is available uh you know not square occupied in the moves for that uh win positions.first again same code we just did before if that space is available that's the position i want uh the computer to take and then we're returning so nothing below this gets executed okay so run that there's a lot of copying and pasting um now we should block we should check to see if it can win but if it can't win it should check to see if it blocks and i'm gonna try to win so put it in the middle okay so now it should block me in the upper left there he did it blocked okay now i'm gonna go over here and it should try and win there you go it won on the left try that one more time okay in the circle there okay it should block me on the right it blocks me it should block me again in the lower left cool and then now it should block me in the upper left cool and now we tied draw there you go so that's pretty smart ai right it's going to win if it can block if it can't one little extra level to this ai is if it can't win it can't block but if the middle square is available take the middle square right that's the most valuable piece of real estate so code to get take the middle square is quite simple actually okay for this we're just going to use the is square available right so if is square occupied actually except this one remember it's not because we want to know if it's available not if it's occupied so you just put the knot in there in moves for index four now we're going to clean this up a little bit if that square is available then i want to return four right that's the position i want to take if four is available take four however four is what's called like a magic number meaning like somebody reading your code later or maybe you six months later might not know what four kind of means even though it is kind of obvious middle square but in general for these like magic numbers you should kind of say let center square equals four and then now use uh center square instead of four that just makes it more readable so people know exactly what's going on so if is square occupied you know for center square return this the center square here okay so let's run this again test this real quick and uh i'm not so we'll just test this right away so my first move is not going to be the center square they should always go to the center square if i don't take it cool and they should block me i'm going to block them i'm going to block them i'm going to block them so now our ai has its four steps it's pretty done so now it's going to be tricky to beat we have to rely on that randomness to make a mistake in the last step so i'm going to try to win right x i'm trying to get them two ways here okay yep so uh no no i did get them okay so it's not unbeatable but again you're gonna tie a lot of the times as well so that is it that is our ai let's kind of walk through this real quick because the ai can be tricky so yes let's check for the win and again how we're doing that big picture is we're just basically extracting all the indexes for a certain player comparing them against the wind patterns and acting accordingly doing the same thing uh if we can block and again we're going to refactor this cam blocking can uh win in like the same function because they're super similar right and then you can see how easy it is just take the center square and then take a random position so that's a basic you know tic tac toe ai now if you don't care about the refactoring we're done you have your app i recommend you stick around for the refactoring and the re-architecting in the model view view model uh there's a lot of good insights in there and because as you can see like if this is your code uh this is this is like hard to read code i mean it's not terrible i've seen much worse but it's just a lot going on on this one view and you'll see once we create our view model and refactor things out and write things nicer uh you're gonna you're gonna appreciate how how good it looks all right refactor time one of my favorite things to do in the world is refactor and make things look look neater so uh the first thing we're going to do click on content view uh right click refactor rename and then also if you click this little uh plus button here you have to like hover over content do you hit the plus button that way it renames the comment as well uh we're just going to rename this game view rather than content view hit the rename button in the upper right i like to do a command b just to make sure things are copacetic in our tic tac toe folder here on the left do a command n we're going to create a new swift file because we're going to create our view model so we're going to call it game view model and the whole idea here just in case you're not familiar with with the view model in mvvm is if i go back here is our game view should be nothing but ui should be very straightforward and simple to understand what's going on and then all this logic right all this what happens on the tap gesture all these functions like all the logic should be in the view model that's where all like the business happens right the ui should be really simple to read and you'll see it's it's very easy to kind of like understand what's going on on the screen okay so in our view model here we're going to import swift ui and this is a class right so final class final just means you can't subclass it so unless you plan on subclassing your view model which you shouldn't make it final call it game view model and it is going to be an observable not observed be careful observable object that's in swift ui so basically what this means is anytime things change just like our state variable in game view it's going to publish an update and you're going to see how this all comes together okay so that is the basic structure let's start yanking things out of our game view into our view model so back to game view well first thing is this columns i said from minute one that was that was gone uh but that is going into our uh game model game view model i'm sorry there you go same thing and then now in game view we're doing command b i'm gonna get rid of my canvas here uh just temporarily we'll bring it back so now what you have to do well first of all i gotta i gotta connect my my view model to my game view should have done that before i deleted that but whatever it's fine so state object this is new in ios 14 uh private var view model so we're going to call it and that equals game view model and we're going to initialize a new one so state object is similar to state variable right swift ui is going to maintain this class so we can make changes to it and update that's why all kind of like your data lives there is because it's a class whereas like in swift ui everything's a struct that gets like destroyed and recreated so your data may not persist well game view model as we saw here is a class so your data is going to persist here in memory so that's why you want to do this here so it's a state object but now in order to access anything on our game view model we have to preface it with viewmodel so here we can't just go for columns we have to go for view model dot columns and now you know everything works fine uh some people like to do just instead of typing out view model i have seen people you know do vm like they'll name this vm just like that and then that way you don't have like it's kind of shorter in the middle i i like to type it out to be honest with you but it's personal preference if you want to make it short and do that cool but i like to actually like type it out so the next thing we're going to do is move all these state variables over again you're going to see how nice and neat our view is and how easy to read so yank out all these state variables into our game view model put them here and now but they can't be state right uh because that is a switch ui struct thing so they need to be at uh published right and i'll explain this here in a second so command c command v maybe cool and then they can't be private either right private means you're restricted to that file we want to access our view model stuff from our view so they can't be private so publish var so again anytime alert item changed publish is going to fire off and anything on game view that's listening to that published or accessing that will update so that's kind of the connection that's being made if something is marked published right because we want that we want any time is game board disabled to change we want to update our ui same thing with our moves array so now the next thing we're going to do in game view is yank out all of our functions right remember i said all these functions uh can go so command x and you're gonna get some errors in the mean time we're gonna come back and fix that anytime you're in the middle of a refactor you break things so now all of our functions are over here right we got our isquare occupied we have our determined computer move positions you know check when condition check for draw all that stuff reset game all here and this is this a lot of this works right away because the stuff we're accessing like well a lot of stuff is getting passed into the function so you don't really need access to it but you know all of our variables are also over here as well so the other thing we're going to bring over in game view so you see it's starting to get smaller but what what is the last remaining like chunk of code here right it's all this stuff in the on the tap gesture right we want that to to be more readable so i'm going to call this something like process player move um what what i initially thought i wanted to do i wanted to have a function called like process human player move which is all this stuff and then process computer player move which is all this stuff but you'll notice the way this works is we return at various levels based on like like it's like a waterfall right like you have to pass this check to get to this check you have to pass this check to get to this check you have to pass this to get to this so you can't really like separate it out so what we can do is make sure i'm in the right stuff here yep i can do a command x to get rid of all that and in our game view model i'm going to put this at the top as well we're going to do funk process player move and we need to pass in the position so for uh position right because remember we used i a lot on our tap gesture we need to know which one we tapped so i'm going to need to you know pass that through here so int and then i'm going to and then i'm going to command v to paste all that in and this should kind of like come over nice and neat or not um okay what what happened here uh oh okay so i right so that's when you do i called a position instead of i because i felt like that made more sense so anywhere i used i i need to uh paste in position i know i i okay you're probably yelling at me like i'll do edit all in scope do that do that and i i always like forget to do that anyway so what i'm missing here now in the game view is i gotta call this right so i got a call viewmodel dot process player okay i'm not getting auto complete all these errors are swift ui is bugging out um okay let's fix the game model or this view model because this should work i don't know what's going on here okay yeah so basically because i'm in this dispatch q domain.async um closure it's saying i need self but new and i think swift 5.3 it might be new in uh if i click this like fix it you can see i can capture self explicitly to enable implicit itself in this closure so instead of writing self everywhere in all this code i can basically capture yourself up here in a capture list and let me do command b to get rid of some of these errors i'm still going to have some errors i think or not um now i don't have to have like self dot is game board disabled self dot alert item so you know what i mean because i'm capturing it explicitly in here and you may be wondering why not weak self um i believe when you do you know dispatch these dispatch queues um you don't have to do weak self 98 sure on that somebody could double check me but i'm pretty sure you don't have to so where are my three errors now nothing in my view model must be back in my game view yeah okay exactly so uh what i want to do here is on tap gesture hopefully it gets model complete i want to say process player or i gotta do view model i forgot view model dot process player okay they're not doing that for me let me fix these other errors uh before i do that so basically what i need to do i'm going to copy the view model and i need to just paste view model dot in front of everything so view model dot alert item view model dot reset game oh and here i have moves up here in the image so view model dot moves okay now i got all my syntax highlighting back hopefully xcode is not mad at me anymore hopefully i get some auto complete now in the tap gesture help me out view model oh dot pro there we go process player move four i remember i said we were passing in the i based on like whatever one we tapped so do a command b to get rid of these errors so now we're almost done almost done with the refactor but you can see well i got to do some cleanup that's the thing every time you refactor always always go back and like make sure your spacing's lined up like things aren't like all crazy out of whack but you can see now this is our view now it's getting getting nice and short we're going to refactor a little bit more but now this on tap gesture let me refactor these two images real quick so you can see the final refactor and then i will explain my philosophy on refactoring and why it's so valuable so okay so this circle here uh right is the i can pull out my canvas again now that we kind of got all this clutter out of the way uh hit resume so this circle right is our red circle or square or whatever you made it right i can refactor this out by the way if you command click on circle hit extract subview that'll do some refactoring for you uh you got to rename it so we'll name this game square view even though it's a circle i'm going to call it a square because people know tic-tac-toe is like squares but whatever but you can see i need to pass in my geometry right because this relies on the geometry reader so what i need to do is have a variable on this var proxy the reason i'm calling it proxy you'll see is because that geometry that i pass in is of type geometry proxy right just i just know that without going into a full lesson on geometry readers uh that's what it is so now instead of geometry dot size i can do proxy dot size again same thing just a refactor but now when i use game square view up here you see it gave me extracted view i need to change this to game let me actually get rid of that square view and you can see it's going to say hey i need a proxy well the proxy remember just geometry that we have up here on our geometry reader geometry there we go and then now same thing for this image again command click on image extract sub view and down here it always puts it at the bottom now i can have i'll call it player indicator right and i'm gonna need a var uh system image name which is a string right because that's what we pass in for the sf symbols so instead of all this like view model moves eye indicator or uh well i'm going to copy that because i'm going to need to pass that in but it's going to be system image name right now so that is our player indicator right we pass in whatever system image name we want it's resizable has all the modifiers so up here instead of extracted view again we do player indicator and then initialize it and you see i need a system image name and i'm going to do a command v because it's the same logic we had right it's going to look at our view model uh the moves and if if there is a move at that position go ahead and use the indicator there's not a move with that position just put a blank uh right there so now our view is looking you know pretty clean right we have our our grid zero to nine that's our tic-tac-toe board we have our z in the tic-tac-toe board we have our z stack which is a game square view and then a player indicator view on top of that z stack which is our x or o or blank and then when you tap one of these you process player move so now that i've kind of like talked through that this is why i like to have my swift ui views you know like this uh you can probably even argue that you could you could almost like um refactor out this entire like grid into something as well and just call it tic-tac-toe grid sometimes you can take this too far but my point is i like to have this read as like a table of contents right because just looking at this i can see the lazy v grid zero to nine i can see the z stack i can see what happens on tap gesture like i get a big picture view or an understanding especially when i have the canvas right next to it as to like what's going on on the view and then i can choose if i'm curious about oh how do you process player moves okay there you go and i can command click on process player moves jump to definition and now here i am now i can dig into that if i choose right and i can kind of like slowly dig but all that code is not out here cluttering everything being so confusing right again it's a table of contents you give the reader of your code all the chapters and say which chapter do you want to explore and you know by seeing all the chapters like this they can get an understanding of what's going on if they choose to explore deeper uh they can so let's kind of review this real quick yep we have our player our move down here and you could if you wanted to take this further you know you could like refactor this out into a new file called like model or a new folder called model you know these could be uh moved out into you know a file called custom views or you know what i mean you can organize your your project however you want they don't all have to be in here um and then yeah the game view model is also refactored as well i'm actually going to make it smaller just so you can see like the overall structure of the file um but yeah this is there's a lot going on here not gonna lie there's a lot of uh code here what i would probably do remember i always said i wanted to differentiate between like the human uh and the computer move because admittedly this is a little confusing but sometimes uh you know it's tough to refactor so i would just this is where i would use the comments uh human move processing and then you know here is computer move processing right that way you can kind of like let them know i try to be sparing with my comments but if i think something is is not readable i put a comment in there and again you get your area conditions and then you know what you do when you want to win what you do in a block so yeah that is uh tic-tac-toe i know that was a long marathon video hopefully you stuck with it but these are portfolio projects right and the idea here is for this to be your base redesign this um maybe add a way for the player to select difficulty medium hard easy and then you adjust your ai maybe you add a two-player mode where instead of playing against computer ai now you're playing you know another human and maybe you keep keep it running score where you save in the user defaults that's the idea take this project build upon it to make a kick-ass portfolio project that you can show to people and get hired so i hope you enjoyed it if you like my teaching style presentation style i started creating my own ios developer courses where we go even deeper than this you can check it out at the website on the screen see in the next video
Info
Channel: Sean Allen
Views: 20,946
Rating: undefined out of 5
Keywords: Swift, Swift Tutorial, iOS Developer, iOS Development, Swift Code Tutorial, Swift app tutorial, SwiftUI, SwiftUI 2, SwiftUI 3, SwiftUI Tic Tac Toe, Tic Tac Toe in Swift, iOS Dev Portfolio projects, how to build an app in SwiftUI, SwiftUI Tutorial, Build an app in SwiftUI, SwiftUI MVVM, SwiftUI Model View ViewModel, Model View ViewModel, SwiftUI MVVM Tutorial
Id: MCLiPW2ns2w
Channel Id: undefined
Length: 78min 34sec (4714 seconds)
Published: Fri Apr 30 2021
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.