Build a Java Desktop Application - Full Course (Sudoku)

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
hi this is Ryan and you're watching my tutorial on Java in a moment we are going to build an entire application from start to finish and I'm going to be with you typing it out and explaining how it works the whole time now this video is a free complement to my course working class Java I prefer to teach people by building things because that's mostly how I learned I don't have a degree in anything but I want to make sure you have answers to questions like when exactly do I use an interface or an abstract class is there really such a thing as the perfect software architecture chances are you want the answer to that question because that is the most common question I have been asked from the thousands of people that I have taught over the years so in this video we are going to write code and solve specific problems I will include overlays which will point you to lessons in the course which explain in simple language and I do mean that virtually every concept from absolute beginner to things which even senior Java developers are not sure on believe me if you make it through these two learning resources you will have everything I wish I had as a beginner all I ask is that you follow along with me during the practical lessons even if they do not make sense at first sound fair enough great let's get started I don't care whether you are a UI UX designer an app developer or a database administrator every app you build starts with some form of problem domain analysis BOM domain analysis which I discuss in detail in working class Java can be summarized by asking two simple questions what data does my app need to represent what problems does my app need to solve for its users in this lesson we will build the first few classes for the Sudoku game these classes are very much like the foundation of a building as we will build the rest of the application on top of them so I've created a new project in IntelliJ IDEA and I'm just to correct the package name here and we'll delete the sample FX ml file that's one way in which you can build a user interface in Java FX but we're going to do it programmatically in Java instead yeah we won't need that controller class and also we will work on creating the main class properly in a different video when we talk about application containers so what we're going to focus on in this video like I say is the classes within the problem domain so the first one we're going to build is just a virtual representation of a Sudoku game as you can see here so this thing here gamestate is actually going to be an enum that we will create in the next lesson represents different states of a game it's my codes usually pretty easy to read I represent the actual Sudokus grid the puzzle itself as a two-dimensional array as you can see here and this is actually just a constant which I use throughout the application you'll notice that I forgot to write int I will fix that eventually but yeah 9 of course represents the number of squares in a Sudoku puzzle now I'm hitting alt insert here and that's allowing IntelliJ to open up a dialog to generate my constructor and some getters for the getter for the grid state I'm actually going to make it sort of immutable and what I mean by that is we're not actually going to just return grid state here we're going to return a copy of grid state a little mistake there how I'm going to do that is I've I will we will eventually create this class called Sudoku utilities which has a static function or method which will create a copy of that given array this basically protects this Sudoku game object from being messed with or changed over time and then we're also going to add implements serializable here this is just so that I can read and write this particular game data file to a file on the operating system easier we'll talk about all that later and there we go fixing the mistake there so the next thing we're gonna build is a little class here called coordinates and it's just going to contain an X and a y value and some interesting functions which we will use with our hash map implementation when we're at the user interface by the way there's a whole bunch of things going on here for beginners there's you see private you see final you see static I talked about all of this stuff in working class Java so do consider checking out the course so we're just going to make a constructor and some getters would be useful as well and then we're going to also add in some functions now or what say functions I mean methods every heir of their language basically calls them functions so what we have here this equals method comes from the object class in Java which every every class extends from object implicitly and the reason why we're doing this is we want to be able to store these coordinate objects in a hash map we're actually going to basically be using them as keys to key keep track of different UI elements anyways I'll talk about that more when we actually get to it got a little a reference to some Snoop Dogg here this and that it's like this and like that unlike this Sena sorry I apologize I grew up on gangster rap alright so yeah nothing super interesting going on here just a whole bunch of boilerplate code do try to follow along because as you can see here I'm not copy and pasting anything and it is my intention that you follow along with this tutorial now the other thing we're going to implement is a hash code function so basically what a hash code is it's kind of like a unique identifier which is generated from some kind of data that you give it so what we're gonna do is we're just gonna take the x and y value and we're going to use this objects hash function and what that's gonna do is it will generate a unique identifier from the specific X&Y value of the specific coordinates thing like I say we'll see more about that later and then you might be wondering why am i creating an interface here I actually decided on this interface during my problem domain analysis stage a cool way to use interfaces is to is to design parts of your application upfront ahead of time this is called design by contract or code to an interface or whatever also I'm anticipating i/o exceptions because this is an i/o device and yeah this is just basically read and write nothing else going on here if you're wondering about interfaces I go into great detail in them in part 4 of my course that's all for now as discussed extensively in part two of working-class Java we can ask a few simple questions about our data to best determine how to model it two great tools for modeling data which does not change throughout the runtime of the application our static final variables and enums the gamestate enum is rather self-explanatory it basically represents different potential states that the game might be in so we have complete active and new and we will see in later parts of this tutorial or a lesson series here where that kind of comes into play we're also going to make a couple different constants for different messages which will pop up in the application in this case this is just going to be a message that will pop up when the user wins a game and I'll have like a little dialogue alert dialog pop up I'll show you how to do that when we build the user interface and it's just gonna display a nice message here congratulations you have one new game and we'll also do a quintessential error message here always good to let the user know that an error has occurred also it took me a minute to remember how occurred was spelled and there's only one thing left to build which is another enum this time actually just selecting them there and this one's called rows all this is for is legibility concerns and you will see how that helps us way later on that's it for this one every application needs an entry point we will now set up an entry point for our Sudoku application in JavaFX now we're going to rename this class main here to Sudoku application and you'll see in a minute we'll actually create a new main class now this main class here exists purely for convenience to be able to actually launch this application from IntelliJ IDEA the reason for it is if we try to actually just run launch our sodoku application then there will be a crash so what we do is we invoke from here first and then in this main method we will actually call the main method within sodoku application which we will see in a minute but this is purely for a convenience sake some lightning fast typing skills here eh so within sodoku application other than fixing some typing errors we're just going to get set up here oh sorry didn't mean to yell so the first thing we're gonna add here is a reference to AI user interface contract dot view which actually is the user interface notice how I like to prefix interfaces with AI and then I will suffix their implementations with UI imple energist impulse pacifically so UI imple will be creating very shortly which represents the actual user interface this stage object that we're passing to it is basically a simple window which JavaFX gives us so that we can modify it and the sodoku build logic class will be actually writing that class at the very end it contains the build logic which is kind of like what you would see with a dependency injection type set up the code required to wire things together now that operation could potentially fail so we are ready to catch an i/o exception if that does actually occur also I didn't have to actually write the main method in this application class because it was automatically generated by IntelliJ IDEA but I'll just scroll down to show you you'll need to have that setup down there in case you're writing this all manually so the next thing we're going to do is we are going to figure out how to import Java FX into our IntelliJ project here so go to file project structure click on libraries hit the plus button hit java and then what you do is you'll select the lib folder in the Java FX SDK you will have to download that but that's all we're doing here go ahead and click OK and I hit OK again and now you can see application has been imported properly and we're all done ok we are now going to build the user interface for this Sudoku game please be aware that I designed the user interface ahead of time using envision now before we get to actually implementing the user interface I actually like to design it and the class that will talk to using Java interfaces now if you have any confusion over when to use interfaces or how they work and the same goes for abstract classes I explain this very and very simply in working-class Java and I really mean that I don't use a ton of words to explain it it's really quite simple if someone gives just a clear explanation which is pretty rare anyways so what we've got going on here as you can see I've actually nested the event listener interface in the I user interface contract basically what I'm doing is I'm using the parent interface as kind of like a namespace or a way of differentiating different interfaces so if I had multiple different user interface components or features this is how I would differentiate them because most of them would have like an event listener and a view and then the I user interface contract kind of points to which part of the application for the UI that we're talking about so it's kind of a handy little trick there the event listener will be something like a controller or a presenter I like to name things based on what they do because typically that's the best kind of name you can give something as for the view anyone who's familiar with three-layer software architectures will know that that is basically a name for some kind of part of the application which binds to the user interface that's typically what it means anyways I also talked about three layer architecture is a whole bunch in working class Java including answering the age-old question that I get asked all the time which is the best architecture is a mvvm MBP MVC and there is an answer it might not be the answer you like but I think I have a pretty good answer to that question in any case I explain the fundamentals of all the different types of architectures preferences aside now do notice I wrote a capital message and show dialog there I will fix that eventually but that was a little mistake so what we're making now is basically a special custom text field so text field actually comes from JavaFX just be careful when you're importing these things that you're not importing the java.awt that's a different UI library that we're not using so the purpose of this text field is all it does is it maintain an X and a y-coordinate essentially or just a value and there's another purpose which is that as a see in a moment by the way I'm gonna use code generation pretty heavily here because this is just not the kind of thing you want to be typing by hand honestly if you want to do that I've mentioned it before but in IntelliJ anyways you can hit fault insert and that will bring up the menu so I'm gonna override two functions from text field and the reason for this is that if you don't override these functions then you get kind of strange behavior with how can I say it when you when the user enters a number zero through nine into a Sudoku text box I would get like repeat numbers and just weird behavior so basically what this does is it makes it so that if the user just hits one on a text field then weird things won't happen and it will just be a single one that's actually entered into the text field hopefully I explain that right now oddly enough there's actually a second method which I need to override now we'll see that in a minute by the way I'll talk about reg X you can see that square bracket zero through nine that is what's known as a regular expression you're going to want to look that up and know a little bit about it it can be really ugly to write but it's basically a way of matching strings so what that means above is we're saying if the string s matches any of the numbers zero through nine then that expression will evaluate true raiga can get really ugly but it's basically just a way of matching text and stuff like that there's other ways you can do this but reg X although it's annoying to write sometimes it's usually pretty handy in that way this is the other function for some reason I needed to override both replace text and replace selection and if you're wondering about the super key word that basically just calls the method which I overrode from the parent class okay so now we're going to build the actual user interface here now this is gonna take a long time user interface code very often is quite tedious to write and for something like a Sudoku game with so many different UI elements that certainly applies so we're making it extend the view con interface that we just created event handler and key event come from JavaFX so that's actually actually how we listen for input from the keyboard from the user interface all I did there is again I clicked on the red text and I hit alt insert and then selected generate or implement methods from the interfaces so again I'm just letting the IDE do some of the work so what I've got here so stage comes from JavaFX again it's basically like the window the background window for the application and group is somewhat like a div if you're coming from like an HTML background a view group if you're coming from an Android background it's basically just a container of some kind now here's an important question how do we keep track of 81 different text fields now I'm going to demonstrate the wrong way to do it or a very tedious verbose awful way to do it one way you could do it is you could have a reference variable for every single text field so basically what we would do is a little bit of an error there my apologies something slipped through the editing basically we could do it like that 1 2 3 4 all the way up to 81 that would be an awful thing to do it would be incredibly verbose so I had to figure out a way to keep track of these 81 different text fields without doing that so what I settled on doing was using a hash map now you if you've been following this an order you'll know that I overrode the equals and hashcode functions for the coordinates and you'll also remember that the Sudoku text field that we just created has an X and a y value so I'm going to store a wrap princes to all of these different text fields within a hashmap and we're going to use that hash function to retrieve and store them so you always want to be keeping an eye out for things which are just more elegant solutions now this event listener class is going to be like the controller or the presenter so this is what we will pass events which the user causes or propagates to and the listener which is basically a control logic class I call it will basically interpret those events and pass messages between the view the front end the UI and the back end so there's just going to be a little bit of boilerplate style code here now one thing in particular because this is a single this application lis as a single feature I'm including the style information in the user interface here if this you this application had multiple different UI screens I would not put it here I would put it in a separate file and make these things basically either protect it or public access so that I'm not basically redefining them in multiple different user interface implementations but yeah they're just stuff that I use to build the user interface how I want so window Y and window X dictates the X and y position in the screen like the actual monitor of the operating system or computer padding we'll see that later it's kind of like the padding between the window and the actual Sudoku board and the X&Y are kind of a again those are going to be x and y values within the window itself and the ones I'm writing our pretty self-explanatory I'll of course show some images of the actual Sudoku board so you can kind of get an idea of what it looks like and I used this string here just for a title okay so we have a lot of work to do here now I think what I'll do next is create the constructor I'm just moving handle down to the bottom that's just to make it look more like the application which has already been written on the side which I am basically copying from and if you're wondering why I'm copying and doing voiceover it's just a lot easier for me to teach and do that at the same time also I am NOT trying to go through this super quickly in case you're wondering why I'm moving so slowly okay so this is a constructor and we assign our stage we create a new group and then we create our hash map and then once that's done we jump immediately into a helper method I don't want my methods to be any larger than they need to be this is actually something I go into in part 5 of working class Java which is how to use how do we think about software architecture in terms of methods which in a sense just avoid giant methods by using lots of helper methods is what are what we're talking about here so what I'll do is for every kind of user interface component in this particular screen I'm going to create a helper method for it and then obviously within that helper method we will actually create it background title sodoku board text fields and the grid lines of the Sudoku board and so stage show is what's going to actually reveal the user interface once everything has been drawn and configured and set listener so what will happen is we'll have our build logic which will write in just later on basically assign our control logic class which is again like a controller or our presenter to this particular view obviously it'll be through the interface and yeah here's where we get to the interesting stuff so like I say this is going to take awhile there's doing user interface code is very often quite tedious so the first thing we'll do is we'll draw the grid lines the Sudoku board these aren't especially in top-to-bottom order so x and y will be where we start to draw these grid lines and obviously so as you can see they're basically we're going to be drawing the grid lines and just notice that the some of the grid lines are actually thicker than the others so the ones which separate the different squares are thicker and that's where we get this if index equals two or five we want the thickness to be a little thicker great alright so when we want to actually draw these lines we're just going to use the rectangle class from JavaFX and what we'll do is we'll draw the what's going to happen in this while loop is we will draw each line we'll do the vertical lines and also the horizontal lines and obviously we'll implement this getline method in a minute so if you're kind of wondering about what we're doing with the x and y etc so we're basically drawing a line and then moving it over by 64 pixels or units whatever the units are and then we will multiply that by the index and then the result will be basically a series of gridlines now the reason why I created this grid line method here sorry getline is that you'll see in a minute will also try a horizontal line and one thing you really want to look out for and this is again something I go into in working-class Java is you want to look for repetitious code so originally I had this code for both the vertical and the horizontal lines in the previous method we were just writing which we'll get back to in a minute and then I realized hey that's basically the same code except the only thing that the changes are some of the variables so whenever you see that that's usually a cue that you want to pull that code out into a helper method and that will reduce the number of lines of code in your project and just make things more legible because you're breaking things down a lot better and all that's going on here is we're basically just creating and styling a rectangle and remember it's going to be an align even though it's a rectangle and there is a plane flying by in the background if you hear some background noise a little mistake there okay so that's the vertical line and then what we will work on next is the horizontal line or lines I should say and so while we're like I say all we're doing here is it's basically the same code we're just going to swap around a couple of the parameters here arguments I should say and that will give us our lines which are drawn horizontally instead okay all right so this is how we actually add these UI elements to the the route the group which is again kind of like a view group or a div and this add all function means we can basically slap any kind of UI element in I think the route the superclass of the UI elements I think it's called a node and so we can add anything which is a child class of node don't quote me on that I'm going off of memory there and now we're gonna draw the text field so this is going to be kind of interesting how we do this so it's going to be a bit of a similar set up so we're going to have like an x and y origin so x and y Delta means the the change in the x and y value and what we'll do is we'll keep drawing these things in increments of 64 units apart from each other you're probably wondering what is that Oh N squared runtime complexity so I don't actually go into detail about this I mention it in working class Java this is an advanced topic here which has to do with data structures and algorithms but I just wanted to mention this this is really important especially for the beginners listening but honestly anyone who hasn't studied data structures and algorithms or runtime complexity what does that mean so there's a bird chattering in the background sorry about that so whenever you're using certain patterns or structures so here we have a nested for loop we have two loops nested with anything within each other now whenever you do nested loops especially if you're doing like more than two what's going to happen here is you want to be very careful doing that in this case it's fine because we're only looping nine times nine times so that's where we get that N squared I can't give you a whole introduction to runtime complexity right now but basically if you have nest loop's and there are different things you can do other than nested loops but that's a really easy example you do not want to be working with large indexes large numbers of elements and the reason why is that the runtime of the application will basically grow exponentially quadratically in this case relative to the number of inputs here so and it was if you want to learn more about that you can look up runtime complexity and Big O notation so this style sodoku tile method here again this is just to pull some code outside into a helper method so I just wanted to take the code which is responsible for styling a Sudoku tile and just put it in a different helper method like I say this just make think makes things more legible in this particular case it doesn't necessarily reduce the number of lines of code but it just think makes things more organized and legible and I'm a big fan of organization and legibility of course this is how we set the font in Java FX lay out X again that'll be like the x and y where the particular UI element will be drawn relative to the window I believe in this case or the stage I think they call it in Java FX the text fields will be 64 by 64 their size and this background empty is going to make them transparent the background will be supplied by the Sudoku board which we'll deal with later so this is how we actually listen for inputs from the user set on key pressed so you'll notice again this user interface simple class implements the I believe it was the event listener or event handler interface from Java FX and so that is exactly where we set that up so we're passing this and the reason why we're allowed to pass this is because this implements event handler text field coordinates is our hash map every time I draw one of these text fields I add it to my hash map and I use a coordinates object as the key so when we use the put function it supplies a key and a value so the coordinates becomes the key the text field reference becomes the value now we're going to draw the snow cout board which is pretty easy it's basically just a colored rectangle of course when you're doing user interface code rarely has anything really easy obviously you've got to set the X and y values and the width and the height and this than the other not to mention the color normally I have code completion on by the way which is a lot quicker you might also notice me making lots of kind of little typing errors part of that is because I have code completion on all so part of that is because I have relatively large fingers and I was born with hand tremors so typing is I can type really fast but sometimes my IQ accuracy is not the best and in this particular case I'm looking on the screen off to the side most of the time I'm type typing so that's why there's so many different errors there it's also because I suck but yeah just thought I would give you some excuses in case you're wondering ok so text obviously this is just going to be the title which goes below the Sudoku board that says Sudoku I believe set Phil actually sets the font color it's not immediately obvious but that's how you do that and again we're gonna add that particular UI element you're gonna see root dog cat children on a doll in this case I just have one element okay now the background is the kind of turquoise colored background scene is again I think it's something kind of like a view group it would basically be like the the background of the background or something equivalent to that and so just a quick point here you'll notice that I'm using very few hard-coded values yeah there are a few like I could make the font size a hard-coded value but I'm using a lot of static final constants and that just makes things more legible and it's also slightly more efficient when you really get down to it computers are so fast right now that certain things like that won't cause a huge it's not necessarily going to make things a whole lot more efficient but it's good to get in the habit of using the more efficient approaches but for me legibility is a primary concern and that's why I'm not afraid to use things like enums when appropriate so for updates square this is what will eventually be called when I just want to update a single square after the user has input a number rather than having to update the whole board which we do sometimes we're just trying to update a single UI element appropriately I kind of took that cue from messing around with react j/s in which it's kind of like a web platform for building user interfaces and our framework I should say and it's neat in that it will only update the UI elements that need to be updated instead of redrawing the whole thing and so text property dot set value that's going to set the actual text input that the user gave number from 0 through 9 update board will wholesale update the entire board so that would occur when the user finishes their game or something like that starts a new game and I forgot to copy the for loop which was kind of silly like I say you've got to understand that I'm my eyes are glued to my secondary monitor while I'm typing list of this stuff so there's gonna be lots of little mistakes like that so for each text field we're going to grab it from the hashmap text field coordinates and I can grab them by creating a new coordinates object with a particular x and y value and the nice thing about the hash function which we talked about earlier is that I basically can recreate it any time I want as long as those values are the same that will create the same hash value which is quite handy obviously get copy of grid state creates a immutable copy of the game which is kind of important I do talk about immutability which is a big word for things which don't change extensively and that's a really important topic if you're especially if you're like an intermediate developer it's a really great way to make your code more resistant to bugs and changes and all kinds of things here double equals error now you'll see the if value equals zero I set the text field to just be empty basically zero or backspace represents kind of like a deletion thing so I had a little error what happened was game state which is an enum we created wasn't actually imported in my Sudoku game so just importing that fixed it so what we're doing now is we're checking if the value either equals that empty ok so actually let me backtrack a bit so what we're doing here so this will this function will basically be called when a new game is created so you can see this game state dot new so what's going to happen is if we have an empty square so if value dot equals blank then what we'll do is we want to enable that text field and set it to a slightly less thick font that was really annoying to do in Java FX so I finally actually had to resort to using a bit of CSS here is what's within those quotes if the number is already colored in I don't want the user to change it so we set disabled to true and we also set it to a slightly sorry I said thicker its opacity so opaque which would be an alpha value if you're familiar with alpha transparency is what I'm saying now that that's done we have the show dialog so this will get called when the logic center of the game the game logic indicates that the game is actually completed properly and then what we'll do is we will show this alert dialog which will then I correct that message thing there we show an alert dialog which basically will ask the user hey do you congratulations you won do you want to start a new game and show on wait will basically pause things and wait for interface our interaction from the user once that occurs we can tell the listener hey on dialog click the dialogue was clicked the user wants to start a new game and then the listener will figure out what to do at that point now if there's any kind of errors that occur so there can be an error which occurs when we either write or retrieve read or write the game state from storage so if any of those errors occur then we just want to let the user know hey something went wrong finally over the handle function again this comes from event handler which is the JavaFX interface so this is where if the user enters a number into a textbox that event will pop up here and what we're doing is we're checking the event type and we're saying okay was it a key that was pressed because I don't really care about other events like mouse clicks and things like that that's just noise if that event matches so here's a funny thing I'm using reg X here as I was recording this code I had this giant if statement full of or statements for the number zero through nine and I was looking at I was thinking didn't I just use regs to do this exact thing so I actually added this in immediately the Reg X and that just clean this method of a whole bunch usually there's very often a more elegant way of dealing with things instead of the brute force method so value is whatever the user has actually entered when they entered a number source will be the UI element the variable the the object that was actually clicked on so that's why I'm gonna use the event get source there to pass that reference and it will get passed to the handle input function method I should say as an object a Java object like the class and we also check for word backspace if it's a backspace then I want it to be treated like a zero essentially so that's why we're passing in zero there so if the user wants to like clear an entry in the sodoku or clear a sodoku square they can either hit zero or backspace I think that's just like a generic thing if the user types anything else then we just set it to empty I just want to avoid writing numbers and stuff like that event dot consume as far as I know what that will do is it will just basically once the event gets consumed then it's not going to propagate through the rest of the application that like the internal code within JavaFX or any of the other stuff and then again we're gonna let the once the input occurs we're gonna actually pass that input to the control logic class which is the listener here and we're going to say hey at this x and y value this value was called okay so we're now going to code the control logic class which is again kind of like a controller or presenter and it basically manages the interactions between the user and the user interface and the back end of the application which would be like the computation logic so it's going to extend the event listener interface which we defined in the previous lesson or video and again I'm going to hit alt insert and that will bring up that dialogue where I can implement the abstract methods now just an important note here whenever you're dealing with like a controller or a presenter or a view model or whatever I strongly recommend that you communicate at least with the back end here I communicate with the view as well the front end class through interfaces if you're not going to use interfaces anywhere else use them across very large and important architectural boundaries across your application this is very simple applied solid principles and it's just a really important thing to do a lot of people think it's a waste of time and I respectfully disagree because the other thing is that it helps me to design my application upfront without worrying about the implementation and if I need to change the storage implementation I can change it quickly and easily without having to cause any issues in this control logic class so what we're doing here is when the user basically inputs a number what we're gonna do or deletes a number we're going to write that to the game storage and the reason why we're doing that is we kind of want the game storage to be like the source of truth in terms of the actual game data now that can potentially fail it it hasn't yet but you just have to be aware that when you're using something like a input/output system you want to code you want your code to be capable of dealing with any errors that might pop up otherwise you're a bad programmer just saying now if that writes successfully then we create a new Sudoku game object the reason why we create a new one is I've designed it that way it's a it's an immutable object and this is actually how you work with immutable data models is you create a new one from the old one once we've successfully written to the storage then we can update the view and again like I said in the previous video if we don't need to update the entire user interface if we can get away with just updating one square that is a very much more preferable way to do things so now what we're doing here is we're just checking to see okay is the gamestate complete and that will be dictated by the code that we'll be writing in the next video and if it is complete then we want to show a dialogue to the user now if for some reason something does go wrong which is unlikely there's not a whole lot we can do beyond showing an error so that's what we do here and this will obviously show like a dialogue to the user so we can at least let them know that something's gone horribly wrong and if the user clicks the okay dialogue then this is where we will handle that and this will generate a new game for the user and again we're going to immediately update the storage first before we update the user interface because the storage is the source of truth we'll be writing the game logic class in the next video and that video is also going to take a little while because it turns out it's actually pretty complicated logic to implement a Sudoku game and once that works successfully then we will update the view know so this is kind of a more functional style of coding at least in the try block and you know as functional as Java can be okay we're now going to write the computation logic of the application and this will take a little while so buckle up so originally I just had a single class here which encapsulated all of the what I'll call computation logic called game logic and then as I went through actually implementing the logic required for the various steps to basically administer a Sudoku game I discovered that there was a lot of different steps involved and I broke this logic down into a number of different classes now the first one here we have is just like a utility class Stokey utilities so what it will do is it has a utility function a static function which will copy the values from one Sudoku array that's passed in one two-dimensional array into another two-dimensional array and then it also has a second function which will take in an old array and it will return a new array with the same values and you'll see that's kind of used in a couple different places in the backend logic of the application and also in the Sudoku game data model itself now before I proceed I need to explain something really important about the usage of static functions here in this part of the application static methods I should say in Java so something that I go into a great detail on in working class Java is how and when to use static methods and static variables and what exactly static means now I can't explain it here because I need to actually explain a little bit about how memory space works in particular the JVM memory space in order for you to really understand how static works I do go into great detail about that in the course now what I will tell you here is a little bit about how to use them safely you're going to notice that I use static methods X and Sallee in this part of the application now the important thing here is that I use them in such a way to avoid what in functional programming you would call side effects as much as possible what I might call shared mutable state problems so what that basically means is that these static functions are only going to reference and deal with either parameters that are passed in so for example old array here or variables which are created within the method body of that particular static function and they can also call other static functions methods I keep saying whenever I say function imagine method now what we basically want to do is the way I'm using static methods here is almost like a I heard someone else say a poor-man's version of functional programming but that's kind of a little bit accurate it's about as functional as you can get on Java so anyways I could go into a lot of detail here and I do in the course but I just want you to be aware you need to be careful when using static functions some people hate them some people say they are horrible and the Antichrist I don't think those people really understand what they're talking about there but there is definitely some truth that there are ways you can use static functions which are really bad and can make it your code difficult to test and all kinds of things so as you can see here again we're going a little bit functional by having these nested method calls so the first method we have here gets solved game will generate a solved Sudoku game this is going to be a little bit complicated this first algorithm here to generate a solved game was made entirely by me the will see later on there was another algorithm which I did not create but this particular algorithm was made by me now the reason why I did this it's not necessarily going to be the most efficient algorithm in particular later on I want to actually rebuild this algorithm as a graph coloring problem which that would be like an advanced topic in data structures and algorithms but I wanted to do something which was hopefully as easy as I could make it for a general kind of programmer to follow along now I'm not saying it's going to be easy but I'll do my best to kind of talk you through it here so the basic idea here first we seed a random number generator that's what random is and then we of course get our we create a new grid which will be based on the boundaries here the grid boundary which will of course be nine so what we're going to do is for the values one through nine we are going to allocate them nine times of course every Sudoku puzzle will have nine ones nine to s nine threes etc so that's what allocations does it tracks the number of times the number in the given value is allocated this interrupt is going to basically this is even a little bit difficult for me to kind of fall along here but what the internet interrupts will do is what we'll see is kind of a backtracking that occurs so if an allocation is attempted too many times then we will add basically we will increment our interrupt variable and then we also have an attempts variable here which works as a fail-safe we'll get to that in a minute so what we're doing is we're saying okay while the number of times we've allocated a particular number so for example one is less than nine keep attempting to allocate that number if the interrupt that is the number of times we attempt to allocate a number is greater than 200 or sorry I think I'm mixing that up a little bit here yes okay so if we make more than 200 attempts to allocate a given number so for example 1 then what we do is that allocation tracker thing is basically just a list of coordinates so each time we will allocate a number we will add that to our allocation tracker so what we're doing here is if interrupts is greater than 200 then we want to go through the past numbers the nine numbers or whatever numbers we've allocated and we want to reset them to zero so basically in simple terms we're backtracking if we get stuck ie interrupts is greater than 200 we backtrack a little bit and we say okay let's just reset to zero and let's try this thing again now it turns out that is not enough it's possible that even doing that that we can just get stuck and this algorithm will run forever that's why we have the attempts tracker as well if a tense is greater than 500 we are definitely stuck and what we do at that point is we clear the whole array we basically nuke everything and reset it all to zero and we try again so this is actually like a two-stage backtracking algorithm we're writing a little helper function here clear array which like I say it's just a simple array traversal and it'll reset every element in the two-dimensional array to zero so as you can see here it's there's a lot of stuff going on like I said before there's actually a way to model this differently using a graph data structure so that's something that I would like to do later on but the thing about that is it's gonna require a certain amount of explanation and practice and I haven't actually solved the problem yet myself so I'll need to do that first but hopefully now that I've talked you through what's going on here it should make at least a little bit of sense and like I say it's not necessarily the most efficient algorithm that wasn't really what I was going for I was trying to make it as legible as possible now what we do is each time we're about to allocate a number to our two-dimensional array we randomly select a square by using the random number generator here so give me an x-coordinate that's random give me a y-coordinate that's random now if that particular grid location does not equal 0 or if it equals 0 we will now randomly or not randomly we will now allocate a value to it if it doesn't equal 0 then we just want to skip it because that number has already been added in so we don't want to overwrite the progress that we have already made now what we're doing here is once we allocate a value we actually immediately check to see if it's a valid or invalid Sudoku game will write the game logic class shortly well at the end of this particular s section and so if the Sudoku is invalid then we want to remove the value we just allocated and then we are going to add we're going to increment the interrupt tracker otherwise if we did just update a new square with a valid value then we want to add that particular allocation that number to our allocation tracker and that's again just to help with our backtracking if it becomes necessary also we want to add to our increment our allocations you and if all that works we return a new grid now that that's done we can work on the unsolved algorithm so this was a really interesting thing to work on so I wrote the unsolved algorithm but there's kind of an interesting problem here how do we actually know if a Sudoku game is solvable or not so when I was writing this unsolved algorithm I thought to myself okay what we'll do is we'll just randomly pick 40 numbers from the Sudoku game and we'll just remove them we'll set them to 0 so I you know initially I thought that probably it would make Sudoku which is solvable it turns out that was not the right intuition at all it's entirely possible to randomly remove 40 numbers and create a puzzle that is not solvable at all so this poses a very interesting problem how do I create a Sudoku puzzle that isn't solved hence unsolved game that is still solvable well what I ended up having to do is so first we do remove 40 numbers from the Sudoku game that has already been solved so that's that's why we pass you'll notice whatever returns from gets all game will basically be piped immediately into unsolved game so also the reason why I'm using Sudoku utilities here is I don't actually want to work on the original object for solved game and that's why we're using the copy Sudoku array values and this is something you want to be careful careful with if you don't really understand Java memory space which is something I explained extensively in working class Java how member verian or not remember but reference variables work anyways so what we're doing here is were removing 40 numbers from the two-dimensional Sudoku grid that's the first step here and similar idea we just randomly picked the 40 numbers you'll notice I want to write solvable with an e repeatedly in this particular video because for some reason solvable sounds like it should have an e Oh English language so what we're doing there is we're just checking to see did we already remove a value there as you can see I completely screwed that up again I don't know how I missed that in the editing well whatever all I meant editing is not my strong suit okay that's a little bit better so if it does not equal zero then we set it equal to zero if it does equal zero then we just loop again without incrementing index just making sure I'm in the right bracket here okay so we remove 40 values from our Sudoku puzzle now the next thing we need to do after we've remove those values is we actually need to solve the puzzle again or at least attempt to solve it so this was actually kind of a big deal because I it's actually not as easy as you might think to solve a Sudoku game it's not the most complicated algorithm that I've written I think probably writing red and black trees was pretty complicated for me that's a complicated data structure but yeah so we're gonna create again an immutable copy to be solved and then we're using that copy function and then we're gonna use this Sudoku solver class which again has static methods to actually check to see if this puzzle is solvable we passed the puzzle in that we just removed the 40 values from and then we want to see that class their Sudoku solver will tell us if it's solvable or not then we're going to jump in and go ahead and create that Sudoku solver now just a bit of background here so the algorithm aim I'm using to solve a Sudoku puzzle is called the simple solving algorithm I actually got this from the Cornell University website there's an article written by called rather solving any Sudoku now I'm not from Cornell or anything like that but I just wanted to give a shout out this is where I get this now part of this algorithm was basically doing what the person who made this algorithm called a type writer in numerate so what that basically means it's a big fancy way of saying we take a two-dimensional array and we turn it into a one-dimensional array so what we would do is so let's say array index 0 through 8 of the one dimensional array would be the top row of the Sudoku and then array index 9 through o my poor brain 17 or whatever the hell the end would be 18 that would be the second row so that would be y equals 2 and then you basically just pull out the two-dimensional array into a one dimensional type thing like that and that's just a necessary step with this particular algorithm now it's kind of an interesting note to see how this is where data structures and algorithms does actually become pretty important because one of the most interesting things I learned when I was studying that topic and I didn't go to university for this stuff if in case you're wondering one of the coolest parts about it was you start to see that you can take the same data the same information represent it in a different data structure and that can make it easier or more difficult to solve problems in particular I guess a really really good data structure for Sudoku games is a graph structure anyways I won't go into detail on that now obviously here what we're doing is we are keeping track of empty cells and we're going to assume that the maximum number of empty cells is 40 so that's why we're doing if iterate iterator equals equals 39 there and so what we're trying to do here is we just want to know which cells are empty that's quite complicated isn't it now I just want to make another reminder here so we're using some nested loops here again you want to be careful when you're working with nested loops because they can if you have two nested loops you've got a quadratic or a square run time complexity if you're using three nested loops you've got cubed run time complexity if you don't know what that is basically just understand do not use nested loops when the index indexes or index I I'm not sure are very large the number of inputs is very large how you would say that technically is when n is large but with the sizes we're dealing with here it's not really a big issue so again we're just checking to see if the actual puzzle is valid at any given point in time so basically what we're doing here is we are checking to see which cells can't be solved essentially and what will happen is will eventually either solve the puzzle or we will implement we will hit an impasse where we can't solve the puzzle I'll be perfectly honest I'm I didn't actually write this algorithm and the inner mechanics of it are slightly escaping me at the moment I think what it is so we get every empty cell and we test numbers in that particular empty cell so we test the number one through nine and we basically just keep adding numbers to each empty cell and every time we add a number we check to see if the puzzle is still valid and so essentially we're using a algorithm which really just follows the basic rules of Sudoku it's going to just kind of incrementally go through it so now we're going to write the game logic class so this game logic class I'm going to be perfectly honest with you originally I assumed I was going to get away with maybe one or two classes in the backend to encapsulate the game logic as you can see things got a little bit more complicated than that in retrospect what I would rather have done here is I would rather have created an interface and then talking spoken to a facade pattern it's called and then that I would have that facade called the different static methods necessary like I say this is one of those situations where I really just thought it was going to be simpler than it actually turned out to be not to say that it's extraordinarily complex but it's there's a number of steps involved in getting this thing to work properly so the result of that is we have this class here game logic which the first thing I did is I encoded the rules to figure out whether a Sudoku game is valid or invalid and that part was relatively easy obviously you just see is any number repeated in a row repeated in a column or repeated in a square or sub grid nine by three by three grid rather and so we have this class here which is basically doing double-duty it's delegating to game generator and it is also encapsulating the logic to check whether or not the Sudoku game is valid or not so that's a bit of a breakage of separation of concerns so if I could do it again and I'm probably going to rebuild this application in Android and I will definitely do a better job with separation of concerns in that particular case it's not a big deal but I'm just saying I'm a bit of a perfectionist with separation of concerns and there is refactoring to be done so another rule we want to know to dictate whether or not a Sudoku game is complete is whether or not the tire tiles are all filled so we can have a perfectly legal valid Sudoku game but that doesn't necessarily mean that it is complete so that's another one of the victory conditions here again I'm gonna be making pretty extensive usage of helper functions to avoid just having gigantic functions or methods the methods were large enough in my algorithms now I mentioned before but squares obviously refers to the 3x3 grids there are 9 3x3 grids that make up the whole nine by nine nine grid in a Sudoku puzzle now you're gonna notice something which might seem a little bit strange so we're going to be checking individual rows of squares now and you're also gonna see this rows top that's an enum I made way at the start in the constants class or package now the reason why I have this enum here I don't want you to be confused by it it is entirely to improve the legibility how we're going to select for the different squares and different rows of squares in this particular in the puzzle is we're going to have to use coordinates x and y-coordinates now just in my case I found it a little bit difficult to read and that is why I used in ohms here to just kind of help remind myself ok we're either talking about the top the middle or the bottom now I think in a general sense that is the main purpose of enums is to improve legibility there's different tools you can use that are more efficient than enums but i there aren't many that are a whole lot more legible than enums maybe like static constants but in this particular case they enum just seemed appropriate what we're going to do here is we're going to check the first second and third square in the top row and then we'll move down and do the middle and then the bottom now if you look up enums in Java then you're gonna see a lot of opinions talking about how you shouldn't use them because they're not memory efficient I would like to just say for the record two things number one enums have been optimized a lot now the thing about performance is you can almost never really say anything totally concrete about performance because operating systems differ JVMs differ are you working with the open JDK or the Oracle JDK or whatever so that's the first problem secondly it's very very rare that the impact you will have on your program using an enum versus maybe a more memory efficient alternative will actually ever be noticeable or even acknowledgeable to the user now I'm not saying you should use them everywhere in anywhere and make really huge enums all the time but what I am saying here is if you have read something about how enums are inefficient just understand these are people who are basically not really understanding the context of the situation okay we're saying they're inefficient that's very often not true what you're saying is they might be like infinitesimally more efficient than a slightly sorry less efficient than a slightly more efficient option but what it really comes down to is does it affect the users the performance of the application on the user side versus doesn't make your code more legible so what we're doing here is we pick out the say for example the top three squares and we add them to a list and then we're just going to pass that list to our collection has repeats function here and what that's gonna do is it's basically going to look at each square individually and we're just it's very simple we're just going to see is the number one repeated is the number two repeated is the number three repeated etc and that's how we kind of dictate whether or not the particular square is valid now zero can repeat that's fine it's just the other numbers that we're concerned about and this just comes from the rules of Sudoku in case you're wondering and we're going to use a handy little function there to make that a little easier collection stock frequency that comes from the Java standard library okay so now that we've got the top row configured here we will work on the middle and bottom rows now I just need to point something out here there is a slight mistake with the X index and the Y index those are actually supposed to be reversed so the 0 0 0 the 3 3 3 and the 666 those are supposed to be Y indexes which would indicate the top middle and bottom again when you're doing this amount of rewriting a whole bunch of code there's gonna be little mistakes like that but just understand those are supposed to be reversed now checking for whether columns or rows are invalid is very very easy we just look through the each grid each row or column in the grid and we again see if there are any repeat values now this is again something where we're gonna see I create another function called method called rows are invalid and the code is going to be very repetitious and that's actually a sign to me that I should probably apply some abstraction here and basically pull that code into one single method but unfortunately that didn't really occur to me until I was busy recording this thing so it is what it is they're not a big deal but again this is just me being an absolute perfectionist when it comes to abstraction and separation of concerns and all that's going on here for every x value we will add all of the Y values and we will just check to see again using our helper function collection has repeats whether or not it is actually valid and I misspelled collections has repeats so we'll just fix that there and then we'll code the function for rows now like I say this is a place where the codes very repetitious so that's usually a sign like I discuss in my course that there's some refactoring in order to fix that but yeah just thought I would point it out luckily I managed to fix that particular error that would have been buggy and that's basically it so what's happening now is we're just some of the static functions actually need to be public because they're used in different parts I could probably make them protected this was a particular case where as being lazy about my access modifiers but you know in actual like production code I would not just leave them public just an FYI there chances are at some point you've heard the term dependency injection or you've heard about the service locator pattern and you might have wondered what these things are actually about there's a more technical word which I really don't like to describe this but my favorite teacher for software architecture Martin Fowler describes it incredibly well he states essentially that we are applying the principle of separating configuration from use I in my own terms I would say that an object which uses other objects should not also build those objects you should pull the logic required to build those objects into a different class or function or something like that or method I should say so that's basically what's going on here we're taking the logic required to basically wire up all the different actors in our application together just before they launch for the user to use and we're encapsulating that into its own special little area of the application and the good thing about using interfaces is that we can use these interfaces within our build logic itself here like as you can see the user interface is being referred to or referenced as its interface same as storage etc etc now oftentimes we do need to refer to the concrete names when we're creating things like a new local storage imple but yeah you get the general idea here so visco what this class does is we attempt to get the game data from storage if it exists now if it doesn't exist then what will happen is the storage class which will write in the next part will throw an exception an i/o exception if that's the case then all we do is we just ask game logic for a new game we update storage if possible and then down below here is where we will actually bind everything together so the first thing we're doing here is we're creating our UI logic class the control logic class and that thing requires storage and our reference to the user interface in order for it do its job properly just gonna import that by the way I'm hitting alt enter to import there and then we need to of course bind the control logic class to our user interface so that they can talk both ways finally we call this method which will basically update the board with its initial State and things will start it'll start the ball rolling that's basically it okay so the last thing we'll do is we will implement our storage mechanism that's often called a persistence mechanism persistence just means persisting data in case you're wondering that could be referring to something like a database in this particular case what we're doing is we're actually just going to be writing a file to the local file system in order to store the data so of course I have my I storage interface which I will implement by hitting alt enter and we have two methods which we need to implement now what we'll write up here is something which is a file type reference variable now basically what this is it can be a bit confusing but it's more like a path or a URL rather than thinking of it as the file itself so system X dot get property will retrieve the operating systems kind of home directory and this child argument here will be the actual name of the file that we store so of course in the users home directory we will store a file called game data dot txt now we'll use this file object both for storing and retrieving the data in case you're wondering so the first method here update game data will of course write to the local file system in order to do that we will use output streams so the first stream will be a file output stream and streams are basically just ways it's about streaming data either in and out of the operating system or there's HTTP URL stream in Java if you want to stream data from the interwebs but yeah this is the most common way to like sort of write files and we'll also need to create an object output stream as well I'm going pretty slow here with the typing because there's a whole lot to type here and it's kind of complicated now why are we doing an object output stream as well so our Sudoku game class if you recall extends a I forget if it's a class or an interface but one of the two called serializable and so what that does is it basically allows us to serialize that class which means turning it into like a easy a data format which is easy to read and write in this case to a text file even and that just makes it easier for us to read and write that data there's other things you can do you could read and write to a JSON object but in this case serializable was the quickest easiest way for me to get the job done and that's often a good solution if for some reason the file doesn't exist or rather if if we try to update the game data and we're not able to we're just going to throw an IO exception obviously the message there is a little bit generic but I'm not super concerned about that now when we want to actually read the data from the file system then we're going to use input streams so it's almost identical except we're just using input stream instead of output streams again we'll use the game data file as a way of finding our resource as you can see it's very very similar now if for some reason we can't locate the file generally speaking because it doesn't exist so if it's the first time the user has run the application then we will throw an exception now this might be an important little thing to mention here exceptions don't always need to be critical errors you want to think of your exceptions is basically being of two different kinds so if you're having like a nullpointerexception that's going to obviously crash your application whereas things like io exceptions I'm not sure why I wrote class not found exception that is probably a typo there that should probably say IO exception but if you have something like an IO exception it might not actually be like a fatal error it might be just a way of basically using it like a control statement if it exists do this otherwise do that so you okay I do actually throw the IO exception there which is good so anyways that's basically all we have to do and then everything will work beautifully thank you for joining me in this tutorial if you would like to learn a lot more about programming and in different languages than just Java then check out my other tutorials and courses available across the web I maintain a YouTube channel where I host live Q&A sessions when I can and I am active on many different educational platforms such as you to me skill share and more this tutorial is dedicated to my friends Haemon dewater Marek de ho bitsy Chetan guard Jaime Corkhill Al Warren and the countless other people who have helped me build my skills as a developer and to share that knowledge with others peace out y'all I'm Odie
Info
Channel: freeCodeCamp.org
Views: 142,396
Rating: undefined out of 5
Keywords:
Id: qH9mWpYMtYU
Channel Id: undefined
Length: 94min 28sec (5668 seconds)
Published: Thu Jul 02 2020
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.