12 Beginner Python Projects - Coding Course

12 Beginner Python Projects - Coding Course

SUBTITLE'S INFO:

Language: English

Type: Robot

Number of phrases: 5024

Number of words: 29977

Number of symbols: 127102

DOWNLOAD SUBTITLES:

DOWNLOAD AUDIO AND VIDEO:

SUBTITLES:

Subtitles generated by robot
00:00
what's up code squad my name is kylie ying and today in this video i've prepared 12 beginner python projects and i'm going to walk you guys through the implementations for all of them now a couple of notes before we begin here's a list of all the projects these projects are in order from what i consider to be the easiest most beginner friendly to the most complex they'll range from mad libs which is a string concatenation to an unbeatable tic-tac-toe ai
00:30
to photo editing in python in addition you might see me make a few mistakes run into a few bugs during these tutorials the reason why i decided to leave these in there is because i think it's a very important skill to know how to go back and fix your mistakes because everybody inevitably makes mistakes and i thought it would be really good for you guys to see some of my logic when i go back and i fix them and of course if you guys are interested in more be sure to subscribe to my youtube channel kyla ying for coding projects and just
01:04
fun computer science related topics follow me on twitch kylie ying for live streams of unedited coding sessions and follow me on instagram and twitter at kylie wying okay so let's get started in a traditional mad lib you would have a bunch of blanks in a paragraph and you would have somebody fill out those links and then read the paragraph out loud with the words that they chose in those blanks so we're going to recreate this project in python
01:37
using string concatenation so let's talk a little bit about string concatenation in other words how do you put strings together so suppose we want to create a string that says subscribe to blank and this blank is going to be a youtuber so we can create a variable youtuber and this is going to be some string so there are a few ways to create the string that says subscribe to the youtuber one way to do it is we can have the string subscribe to
02:10
and then we can concatenate it with youtuber by just adding a plus sign the second way is to have a string subscribe to and then have these curly braces and what we can do is we can call string.format youtuber and what this is going to do it's going to put the youtuber whatever the value of youtuber is into where the curly braces are in that string and now the third method and what i think is the most straightforward is called an f string and
02:41
in an f string we can define this f string by just prepending an f in front of the string and then we can say subscribe to and then the curly braces and then directly in the curly braces we can add the variable name youtuber so with an empty string let's try running this real fast just to check that there are like no errors and they all turn out to be the same thing so let's open terminal and run the script so here we see subscribe to blank three times no errors okay everything looks good so now let's try what the
03:13
youtuber actually filled out to some string let's just try kylie ying so let's run this again and you'll see that now all three of these print statements say subscribe to kylie ying and so for the sake of this mad lib we're gonna use the last one the f string just because i think that's the cleanest way to express string concatenation okay so starting this mad lib so first we're just gonna assign mad lib variable equals and then an f
03:48
string so let's say computer programming is so blank where blank is some adjective and now we have to define this variable adjective so we can say adjective equals input so here we're going to get a user input and let's do adjective as a prompt it makes me so excited all the time because i love
04:17
to blank let's make that a verb and this break right here this is just saying this is telling python hey this string has gone on to the next line that's all that little slash there is stay hydrated and verb two like you are and let's make this a famous person exclamation mark okay so let's just use that example right there and now don't forget to define these variables verb 1 verb 2 and famous
04:49
person so up here we're going to say verb 1 equals input and the prompt is just going to be verb because all we want is a user to input some verb and now verb 2 is going to be the same thing but instead verb 2 will be the name of the variable and then famous person again equals input so we're getting user input and we're gonna say famous person as a prompt okay so i actually have to remove this space and then
05:19
at the end we have to print the mad lib to show the user so that's it though now we can run this code alright so adjective let's do amazing verb how about skydive and then another verb jump and a famous person captain america okay so our math lib is computer programming is so amazing it makes me so excited all the time because i love to skydive
05:52
stay hydrated and jump like you are captain america and so yeah there you have it that's mad lib in python alright so if you guys actually download my code which is linked somewhere below you'll notice that there's a file called randommadlibs.pi what this is going to do is it'll choose one of these four madlibs that i've prepared and it'll let you play that game all right adjective pretty another adjective soft
06:25
another adjective a pretty glow burst suddenly across the enchanted sky above them as an edge of dazzling sun appeared over the sill of the nearest mask the light hit both of their hand at the same time so that voldemort's was suddenly a flaming water bottle what did i just read anyways there's a mad lib for you first i'm going to teach you guys how to implement a guessing game where the computer has a secret number
07:03
and we are trying to guess that secret number so the first step is actually having the computer generate a secret number for us to guess and in order to do that we're going to import random whenever we call import random it actually goes to this package that comes with python and it says hey all of these functions that are here like make these accessible in our script so that we can call these functions so for example in order to get a random number something like random.rand int might be
07:34
very applicable because it returns a random integer n such that a is less than or equal to n less than or equal to b so a and b are the parameters of this function and we need to pass in arguments i'm going to define a function and i'm going to define this function let's say guess i'm going to make x a parameter so that we can pass that into this random getnumber function so first we need to get the random number and our random number well we're going
08:04
to use random dot and then rand int which is exactly what we saw down here let's make it between one and x okay so now basically what this is going to return is a random number for us to guess okay what's our second step here our second step is once the computer has a random number we need to guess right we need to guess in terminal and input what our guess of the number is and the computer will tell us whether it's too high too low or if we've guessed the number correctly
08:35
i need to keep looping until i get the right answer right so that sounds like a job for loops and basically since we don't have a predefined universe to iterate over we're going to use a while loop so let's insert while in there and now in this while loop we need an expression here right and now for this expression when do we want to stop this loop we want to stop it when our guess number equals the random number so that means
09:07
our expression should be something along the lines of guess does not equal random number then we want to iterate over some things now we need to actually define this guess and we're not going to make a guess up here because we're just trying to initialize the variable tell python that this variable exists so we can go back and change it later so after a random number i'm going to say guess equals zero right because we don't want our guess to ever
09:36
accidentally equal that random number and here if guess is zero well random number is random. rand int between one and x and that means that it will never be zero so while the guess is not equal to random number we're going to get the user's guess so guess equals input guess a number and we can even get a little fancier here between one and so let's use an f string
10:08
and we can do x let's just see what that looks like real fast let's call our function guess at the bottom of our script and then let's just print our guess let's see what happens when we run this all right so if we run this pic guess a number between one and ten let's do five okay so we've printed the number right and i'm just gonna cast this as integer because i want my guesses to be integers so what do we have so far the
10:41
computer has said okay i've gotten a random number and now we've set up this loop where i can keep guessing until i guess the right number but that's no fun right we kind of want the computer to give us some feedback give us some clues into what's right and what's wrong so that means that i'm going to use some if statements and these if statements are going to tell me hey you're kind of high kind of low or oh maybe you've gotten it all right so let's add these if statements in so if our guess
11:11
is less than our random number then we can print sorry guess again too low all right but then else if our guess is greater than our random number then we can print sorry guess again too high and then if it's not less than if it's not greater than that means it's just right it's in that goldilocks zone right and that means that you have guessed the
11:43
jackpot you have guessed that random number and so what do we do then well we actually don't have to do anything because remember this loop while the guess does not equal the random number it does all of this but as soon as your guess equals a random number so once you've input this guess we don't hit any of these if statements so then we come back to while the guess does not equal the number but now your guess equals the random number so it actually breaks out of this loop meaning that at the
12:13
very end i can print yay congrats you have guessed the number and you know what we can even just toss in a random number in there so let's use our f string again yay congrats you have guessed the number random number correctly all right are we ready to play so if we go to terminal let's run our script okay guess a number
12:49
between 1 and 10. um i'm gonna do four okay it was too high so maybe two too low all right so that means if four is too high if two is too low it has to be three right wow look at that i've guessed the number three correctly [Music] all right so we talked about earlier how the computer is guessing our number but we can also do the
13:24
complete inverse of that function we can come up with a secret number and we can have the computer try to guess it so now i'm going to create a new function called computer guess of x all right and in this function let's think about what we actually have to do so i have a secret number and i'm not going to tell the computer what the secret number is right that basically means the computer has a range of numbers to work with a minimum and a maximum a low and a high okay so that means
13:54
let's set the low and the high initially because we know what that is without even having to loop over anything so i'm going to say low the lower bound is one and the high is x because we do have that entire range between one and x to work with initially until the user can provide some feedback we need to be able to tell the computer if it's too high too low or if they've guessed correctly which means that let's initialize a feedback variable all right feedback
14:26
and at first there aren't any guesses nothing's too high nothing's too low so just like how we initialize guess to be zero let's initialize this to an empty string and now basically we want to loop over this feedback expression so while this feedback expression does not equal what we're going to make it represent when it's correct let's do c because c for correct so while this feedback does not equal c well the first thing i need the computer
14:57
to do is to guess a new number so i'm going to make the guess random i'm going to use random.random again and this time we're going between low and high now we don't want it to always be between 1 and x right because we want to be able to kind of change these bounds according to the user's feedback because you know that if something is too high then anything above that we can kind of stop considering and then if it's too low anything below that we can stop considering so that's why i'm passing in these low and high values into this rand
15:29
in so that we can guess a new number between the bounds that we know has to be correct okay so we have a guess and now we're trying to ask the user for feedback hey is our guess right or is it wrong so here i'm going to do feedback equals and let's see a user input is so i'm going to use an f string again so i can put this variable inside my string is guess too high
16:00
and let's make that h okay too low and that's going to be l or correct and that of course is c the user is going to input h l or c i have these uppercase letters here this lowercase up here i'm just going to make this input lowercase so adding that dot lower at the end is going to take whatever this string is from the input and just lowercase it so h l and c are all lowercase if we try to compare capitalized letter to its
16:32
lowercase letter it actually does not come out to be equal so that's why i'm adding this lower in there let's look at our different cases again so if feedback is h so basically we're saying okay if it's too high then that means we want to adjust our upper bound because if our guess is too high like you know if we're guessing out of 10 and we guess eight the other person says oh that's too high that means that nine and ten cannot be the numbers that would mean that we need to adjust our upper bounds
17:01
our upper bound is actually going to be what we just guessed minus one because for example if we guess eight then we know it's between one and seven if eight is too high and now if the feedback is l we know that our low bound has to be guess plus one right because it can't be that low number and of course we can make that an l if alif makes it a little bit cleaner because feedback can only be h or it can be l like it can't be both of them
17:32
and of course if it's correct we don't have to have an if statement for that because our while loop kind of takes care of that so at the very end of course when we exit our while loop that means that the computer has guessed our number correctly print yay the computer guessed your number uh let's put the number in there and so i'm going to put guess in this f string because so that means that outside of this for
18:06
loop this variable guess is actually the last thing that the computer had guessed which means that you know if it's correct then that is our secret number all right so basically one other thing that i've noticed is random.randam will actually throw an error if low and high are the same number so we can do a couple of things we could theoretically put this statement up here that prevents this loop from continuing if low equals high because if low and high are the same number that
18:37
means that you've narrowed it down right if you're saying eight is too low and your new low is nine and then you're saying 10 is too high so then your new high is nine well that means that the computer has actually narrowed down the number to nine but if we break too early so if we're saying and low does not equal high then we don't actually iterate over this loop when our low is nine and our high is nine right we just break and we say oh the computer gets your number correctly but the thing is we actually want the user to say that the computer has chosen it correctly
19:07
so that's why we can't actually have that statement in there instead what we want is we want to say if low does not equal high then our guess is a random number between low and high otherwise so this means if low does equal high otherwise our guess is equal to one of them so let's just say low i mean it doesn't really matter this could also be high because low equal to high all right so then our feedback actually puts this number into here and
19:37
it prompts the user to say hey that's that's right so then at the very end we're saying okay the computer guessed your number guess correctly all right let's try this five oh shoot i need to come uh let's say our new secret number is six so that's too low seven oh that's too high six okay that's correct yay and you know we can even play this with like a thousand
20:07
all right so for our secret number let's actually do the price of ethereum which is approximately 392. okay so python 3 main dot pi 640 that's too high too low too high too low too high close we're getting closer oh 393 that's a little bit too high too low 392. the computer gets our number correctly
20:38
look at that the computer has guessed our number correctly all right so that's it just using some functions and some while loops we were actually able to get our computer to guess our random number and for us to be able to guess our computer's random number so now when we're bored we can play this guessing game woohoo so the next beginner project idea is rock paper scissors this one's super simple but it's a step up from the previous one here we're going to be using random so we definitely want to import random
21:26
and we're going to be using a function so basically we want some user input right because we want to play against the computer so the user is going to put in an input let's use r for rock p for paper or s for scissors and then the the computer is also going to choose and here we're going to do random.choice because we have our three different choices rpns so now the computer is going to randomly choose one of these choices once we know the user's choice
21:57
and the computer's choice we can come up with some rules in order to determine who wins so the first rule is if the user and the computer have both the same choice then it's a tie in this game we know that rock beats scissors scissors beats paper and paper beats rock so let's define a helper function is win to see who wins and here i'm going to say player versus opponent and this will return true if the player wins and now we're just going to use this
22:27
little rule that we had to see so if the player is rock and the opponent is scissors or if the player is scissors and the opponent paper or so now we have three conditions right or the player is paper and the opponent is rock then we know that the player has won so we're going to return true so now
22:58
we're going to ask up here if the user has won so is when user or computer so the computer is opponent and the user is a player then return you won actually we're gonna return it's a tie up here and then otherwise we're just gonna return you lost because if the computer won then we lost here you'll notice that i don't have an if statement before this last return and the reason for that is because if you've already passed these two cases and after each of these cases the function ends right here or in this
23:29
case if you won then it ends right here the only way that we can ever reach this line is if we didn't go through any of these which is the same it just saves you an extra line of code instead of saying else or instead of saying if is when computer come a user because the only way we get to this line is if this is true so we don't even need that line so here we're going to print play and here i'm actually going to add a line break
24:01
and say what's your choice now let's see what this looks like what's your choice r for rock paper paper s for scissors i think i'm gonna go with scissors i lost let's play again rock i won [Music] so the first thing that we have to do for hangman is we have to choose a
24:40
random english word so i actually went on stack overflow and i found this very relevant question how to pick a random english word from a list and if you scroll down a little bit there's this like json file that's linked so i'm just going to click on that and when i open it there's all of this text and basically what this is is it's just a very long list of words that uh we can use for hangman so i can copy and
25:11
paste this entire list of english words into a python file and i can assign it to the variable words which we can use in our hangman game later so now i can open a hangman file and i know that i want to be able to choose randomly from this word list so i'm going to import random and then also i know that um i want the word list that i just made and i called my file words.pie so in my hangman file i'm going to say from words
25:42
which is words.pie import words and that second word is just this variable words so now if i print out words in my hangman file i would be able to get that entire list of words that i just copy and pasted so the first step in actually getting our computer to play hey man with us is the computer has to figure out a word for us to guess so we just got this entire list of words into this python file and now we just have to randomly select a word from it but you'll notice if you look through
26:15
this word list that some of them actually have spaces and dashes in the middle of the word which we can't exactly guess in python or in hangman so we actually have to keep choosing a word until we get a valid word that we can guess in hangman so in order to do that i'm going to define a function called get valid word and i'm going to pass it a list of words so the first thing i'm going to do is assign you know
26:44
the word to random.choice words and what random.choice is it takes in a list and it randomly chooses something from that list so i'm just going to get a random word from this list and i'm going to make a while loop saying while dash or space is in this word keep choosing the word so what this while loop does is as long as the statement is true it just keeps iterating back and forth until it's not true anymore which means that
27:16
when it stops iterating we'll get a word that doesn't have a space or a dash in it and then finally we're just going to return that word we need to be able to keep track of which letters we've guessed and which letters in the word we've correctly guessed we also need a way to keep track of what is a valid letter and what is it so now we're going to set that up i'm going to have word letters a variable that saves all the letters in a word as a set and this will use as a way of keeping track of what's already
27:46
been guessed in the word and then i'm going to have an alphabet and basically i'm just going to import this already predetermined list of like uppercase characters in the english dictionary um and then i'm going to have an empty set called use letters which i will use in order to keep track of what the user has guessed all right so now we're going to get some user input so basically what we can do is we can
28:18
just ask for user input in python directly and if we run this in terminal then the user can type in you know a character and we can use that as input so we're going to save that as a letter and i'm just going to uppercase this because i'm just going to do everything in uppercase a lowercase a in python is different than an uppercase a so if you try to test equality between those two strings it actually won't be equal so i'm just going to do everything in uppercase
28:49
and basically if i'm going to i'm going to say okay if this is already if this is a valid character in the alphabet that i haven't used yet then i'm going to add this to my use letters set and then if the letter that i just guessed is in the word then i'm going to remove that letter from word letters so every single time i guess correctly then this word letters which is keeping
29:20
track of all the letters in a word decreases in size and then if this user letter that the user just entered is in used letters then that means that they've already used it before and it's an invalid guess so i'm just going to print something saying you literally just guessed that word or that letter otherwise that means that you know they typed in something that's not in the alphabet and it's not in the newsletters that they've already guessed
29:51
so that just means that they've typed in a wrong character and we're going to print an error message saying you didn't type in a valid character so now that we can get the user input we want the user to be able to keep guessing until they get the word so in this case we're going to be using a loop and loops are basically just a way to you know loop around your code and iterate so in this specific case i want to use a while loop because i want the user to just to keep
30:27
guessing until they actually guess the word and because every single time we're removing a letter from word letters which is a set of the letters in the word that we haven't seen yet i'm just going to keep decrementing that so the condition that i have to satisfy for when the user gets all the letters in the word is when the length of word letters is actually equal to zero so while the length of word letters is greater than zero
30:57
i'm going to keep iterating through this input until they guess all of the letters so my while condition is going to be while the length of word letters is greater than zero iterate so let's just add that in there so before we can actually play this game of hangman we need two things that we need to tell the user so the first thing is what letters they've already used so that they can keep track of what they've already guessed so we're just gonna have a simple print
31:28
statement and then we're gonna say space dot join use letters and what this dot join does is it turns this list into or interval into a string separated by whatever the string is before the dot join so in this case each of these letters will be in a string separated by a space the second thing that we need to do is we need to tell the user what the current word is
31:59
but with dashes where the characters that they haven't guessed are so in this case i'm going to first create a list where every single letter that they've guessed is shown and where all the letters that they haven't guessed are just dashes and then i'm going to take that list and i'm going to join it with a space just like above so that we can create a string using that list
32:29
[Music] in that game i literally could have guessed as many times as i wanted to so let's make this a little bit more fun let's introduce the concept of lives into hangman because usually in hangman you can only guess until the guy is dead right so let's say that live let's say you get six lives
33:08
so the first thing we have to do is if the user has the letter in word letters then you want to remove the letter but if they don't then that's when you want to take away a life so with my lives variable which is set to six at the beginning i'm just going to subtract one there and i'm going to tell the user that your letter user letter is not in the word and then everything else should stay the same now at the very beginning i'm going to say where the um
33:39
where i show the user the letters that they've already used i'm going i'm just going to tell them you have x lives left and then you know they can guess the letter and right now our while loop condition is set to as long as they still have to guess more letters in the word they keep playing but now we have another condition right we have the condition of lives so as long as either a they haven't won yet which is when the length of the word
34:10
letters is greater than zero or b when they haven't died yet so up here we're going to add another condition in this while loop we're going to say while the length of the word letters is greater than zero and lives greater than zero then we want them to be able to guess this means that as soon as they either win when they when they've guessed all the letters then they exit this while loop or when they've died when
34:41
lives equal zero they exit this while loop so at the very end right now we're telling them that they've guessed the word um correctly but now that's not the condition anymore for this while loop we also have an aspect of lives so if the lives equal zero then they actually died so we say sorry you died the word was blank otherwise in this else statement we can say yea you guessed the word so now let's
35:13
try a game of hangman with all these different components [Music]
35:55
now we're going to create a command line version of tic-tac-toe with various types of players so either a human can play or the computer can play so humans can play against a computer humans can play against each other or the computer can even play against the computer let's get started we're going to split up our player and our game into two separate classes so that when we actually play we can create a game and then we can tell the game hey this is my x player and this is my o player so the first thing we're going to do is
36:26
create a file player.pie and up here i'm just going to tell you guys right away we're probably going to need math and random so i'm just going to import them right now we're going to have a base player class and in this class we're going to initialize it with the letter that the player is going to represent and this is either x or o so that's cross or not in official like tic-tac-toe terms so self-doubt letter is going to be letter
36:57
and we want all players to be able to get their next move so here i'm going to say define get move self comma game and i'm just going to pass because well this is our base player class and on top of that we're going to build a random computer player and we're going to build a human player here we're going to use inheritance in order to create a random computer player and a human computer player that builds on top of this bass player object and so in our
37:28
initialization we have to initialize the super class so we're going to say super.init letter and what that's going to do is it's going to call this initialization in the super class which is the player and define get move so in our getmove function let's hold off on this for now same thing for the human player in this human player our super class is still player we're going to initialize the same way that we did the random computer player
37:59
and then we're going to find get move and again self comma game and let's also come back to this let's first go and define the game to see exactly what we're dealing with when we pass in the game let's create another file let's call this one game dot pi so in game dot pi we're going to define class tic-tac-toe and so in this class what are we going to need we're going to need a board right because our tic-tac-toe it's a 3x3
38:30
board let's create that board for our board let's just use a list of length nine that will represent the three by three board and what we can do is we can assign an index in this length nine list to each of the spaces and then that will represent our board what i'm also going to do is i'm going to have this variable self.currentwinner that will keep track of whether or not there is a current winner in this game and if there is who is it well let's first of all
39:02
be able to print the board right we're going to want to see like what's in this board so for each row and here let's split this up into the rows so self.board this is indexing into our length nine list and then this i times three so we're doing i and range three right so this i times three to i plus one times three so basically that's saying like which group of three spaces are we choosing is
39:32
it the first one second one or third one and that that represents the row indices zero one two that's the first row indices three four five that's the second row and then six seven eight that's the third row for each row what we're gonna do is we're gonna print and these are just gonna be some separators so let's add these and then dot join row is just saying like join them in a string where the separator is this vertical line so you guys don't have to worry too much
40:02
about print board because i don't think it actually like contributes much to the logic of the game it's just how do you print this okay and then here for print board numbers well this is a static method because it's it doesn't relate to any specific board we don't have to pass in a self what this means is we're just going to print out which numbers correspond to which spot and so for example here you can see it's 0 1 2 etc and so our number board is going to be
40:33
string and then whatever i is for each eye in range again this is a row right this number board might seem kind of scary but if we think about this for j and range three so that's j equals zero one here this range is j times 3 and then j plus 1 times 3. so this is the exact same range that we saw up here for each row so what this is saying is essentially just give me what indices are in the rows for each of
41:05
the rows this is gonna come out to like zero one two that's one sub array and then three four five that's another sub array and then six seven eight we're gonna concatenate these strings the same way that we did above in print board okay so now let's actually dig a little deeper into the logic of the game given this board we're representing the empty spaces with the space we're going to need to know what are the available moves after you make a move right so we're going to return a list and this
41:40
is going to be a list of indices so let's actually expand this out and then i'm going to show you guys how to do the list comprehension for it let's initialize moves to an empty list and then let's say for i comma x and enumerate self.board enumerate is going to essentially create a list and assign tuples that have the index comma the value at that index so here we have zero comma x one comma x and then two comma o in this for loop we're going through each of these two bowls
42:11
and we're assigning the first item in the tuple to i the second item to x so we can say if x equals actually let's call this spot because that might be a little bit more intuitive if spot equals space then we know that this is an empty space and we know this is an available move so we're going to append that index because we want to know which spaces are available we're going to append the index of that spot to moves and then at the end we're going to return moves another way to write this is a list
42:42
comprehension and that would look something like this i for i comma spot in enumerate self dot board if spot equals space so this is essentially just condensing this entire for loop into a single line it's saying for i comma spot and enumerating the board if the spot is space then put i into this list and then we're going to return the entire list easy little one-liner makes the code clean
43:13
okay so now that we have that function let's define get move for our players so the square that we're gonna choose for the random computer player well we're literally gonna just choose a random spot on the board that's empty so let's just do random.choice which is going to choose one thing in a list at random and we're going to pass in game which is our board game.available moves again it's just gonna get a random valid spot okay so the human player we
43:45
want the human to be able to choose a spot based on some input that we pass in through terminal we want the user to keep iterating until they achieve a valid square initially we're gonna say valid square equals false and then the value is none because the user hasn't input a value yet let's say while not valid square so while valid square is false our square is going to be input and then self.letter so x or o player turn because we want
44:17
the user to actually look at terminal and not get confused by whose turn it is and input move zero through nine so what we're gonna do is we're gonna incorporate a series of checks to make sure that this is actually a valid number that we can put in so here we're going to wrap it in a tranche so for the try we're going to say value so this val equals int of square remember square is this input that the user has input if we can't cast this to an integer if
44:48
we can't cast a certain number so if they input like x y z for square it's going to raise an error when you try to cast it to an integer and then the second part if the value that they give you is not in game dot available moves in the list of available moves then we can raise a value error and so essentially if either one of these things goes wrong then we know it's not valid right if we pass both of those and we can say valid square equals true because
45:18
it's valid then we're going to catch this value error and we're going to say print invalid square try again and so this is going to repeat the loop we're going to get the input for the square again and we're going to repeat this checker at the very end once we've gotten a valid square we're gonna return that value at the end so that's gonna be the human player's next move okay so we have our player so let's now continue working on our game so that we can start playing a game of tic-tac-toe
45:48
we have part of a representation of a tic-tac-toe board let's in order to get the rest of the functions that we need let's define a function called play outside of this class where we're passing in a game an x player an o player and then i'm gonna pass in this extra variable print game that's just gonna be set to true or false and if it's true it'll print out all the steps so this is like if you want to play against it but later on if we want the computer to play against itself or like a bunch of iterations we don't need to see the computer print
46:20
out every single game so then we can toggle that to false if we're printing the game then we're going to say game dot print board numbers right because then we can see which numbers correspond to which spot and the starting letter let's just assign that x i don't know if that's like what tic-tac-toe actually starts with but we're just going to say x all right so now while the game still has empty squares so while the game is still like incomplete we're just going to keep iterating right and we don't have to worry about the winner because the
46:51
output of this play let's just return the winner we don't have to worry about continuing this loop because we'll break out of it with that return statement so in order to check whether the game still has empty squares let's create a function within the class called empty squares pass itself and what we're going to do is we're going to check if there are any empty squares on the board so we can just say return space in self dot board and space and self dot board will become a boolean empty squares will just return a boolean
47:21
of whether or not there are empty spaces in the board and we might need to know the number of empty squares so i'm just going to say okay we can return the length of available moves which will return this list and so we can just count how many empty spots there are um we could also say self.board.com because this is a list self.board.count and then just space so that will count the number of spaces in the board all right so while there are empty
47:52
squares we want to get the move from the appropriate player so if the letter equals o then we're going to ask the o player to get move and if it's not o that means it's x then we're going to ask the x player to get the move all right let's define a function to actually make a move now that we've gotten the player to get their next move we go back up to our game and we say define make move when we make a move we need information about what square the user wants their move to be
48:29
at and then what letter the player is so we know like what to assign that square if the move is valid then we make the move and then we return true if it's not a valid move then we return false nothing should ever be an invalid move but just in case if self.board square is empty so if this this means if at that space on the board nothing's there yet then we assign that letter to that given square and we return true if that doesn't pass then we return
49:00
false okay so let's put this into our play loop so if game dot make move so if this is valid if we want to print the game we're going to print letter makes a move to square blank and some square let's make an f string there and we're going to say game dot print board because we want to see a new representation of the board where this spot has now been claimed by this user and here this is just an empty line
49:36
that we're gonna print okay after we made our move we need to alternate letters so here we're going to assign letter equal to o if the old letter was x otherwise we assign it to x and this basically a different way to rewrite this would just be if letter equals x then the new letter is o otherwise the new letter is x that's exactly what we're doing here we're just switching players okay but wait we don't we're not
50:07
actually checking anywhere if anybody won so what if we won if you think about it the only time that you should win a game is like right after you make a move right if you won if you want a game you should win on that move so we're gonna go back to make move and after we've placed the letter on that board we can toggle current winner to the winner if there is one so let's make another function that will check for the winner so after we've made this move if
50:38
self.winner and then we're gonna pass in our last move because that's the one that's gonna be our winning move right if self.winner then we can assign current winner equal to that letter we'll come back to the winner function but suppose that we have this checker so then after making this move if we have a current winner we can check for the current winner before we switch letters and if there is a current winners which
51:09
means which means that if current winner is not set to none anymore then a letter has won and we can end the game because then we can just return the winner of the game so in this game we're going to return the winner if there is one and if there isn't then we're going to return none so that means none is a tie and we're going to return the letter of the winner so here if game.currentwinner well it was letters turned so we can just return
51:43
letter the letter that gave us the win okay so then also let's just add in the nun for a tie right now so at the bottom we can say if print game then after this while loop is over we can just print it's a tie all right um now we got to go back and actually create this function that'll check for a winner right so we can define winner and then input the square and the letter and we we know that in tic-tac-toe we're
52:19
a winner if there's three in a row anywhere but we have to check all the possibilities whether that's in the row column or the diagonal all right so first let's check the row so the row index which row it's at is going to be whatever square that you give it divided by 3 and then round it down right so that's what this right here is so that double dash is just saying how many times is three going to square row is going to be self.board and here
52:50
we're going to see this indexing again but this is essentially saying given the row index get the row so here row is just going to be a list of the items in the row that we've selected and we can say if all so this is going to be all is saying like if everything in this list is true then this comes out to true otherwise it comes out to false so if all and then within this list we're gonna do another list comprehension so for every spot in the row we're checking
53:20
whether or not that spot equals the letter because that's how we're checking for three in a row so if and then if all the things in this row are equal to that letter so that means that we have three in a row in this row then we can return true if not then we keep going right so then let's check the column next and we're going to use very similar logic so the column index is okay divided by 3 and then take the leftover so that's going to tell us
53:51
which column we were in the column is going to be the self.board and here we're going to do another little indexing trick but if we take the column index and then for every single row so that's i for every single row if we add the column index and we essentially get every single value in that column right and we're going to put that in a list and that's going to be our column so here column is just going to get everything in the column
54:20
where we just moved to and again just like above we're going to use this if all checker and instead of row we're just going to replace it with column so if everything in the column is equal to the letter then we return true okay so now finally if that doesn't come out to true we're going to check our diagonals intuitively we can kind of see that the only way to win a diagonal is if you placed a move that was along a diagonal here we're going to check if the square
54:52
that we had just moved to is actually an even number zero two four six eight and these are the only moves possible because these are the only these are the diagonal spaces so if we assign zero one two to the top row three four five six seven eight it's pretty easy to see that the top left is zero the middle is four and the bottom right is eight and then the other diagonal would be two four and six so that's why here we're checking if the
55:22
square is divisible by two so that that's basically saying it's even then this diagonal one we're gonna say for i and zero four eight so this is the top left to bottom right diagonal so we're going to put the things in the board that correspond to zero four and eight into this diagonal one and then same thing for diagonal two but instead it's 2 4 and 6. so this is the top right to the bottom left diagonal
55:54
and once again we're going to use this if checker and say if every single spot equals the letter in the diagonal so diagonal 1 return true and again for this diagonal 2 if every single spot equals the letter in diagonal 2 we're going to return true and at the very end if all of these checks fail then we don't have a winner so we return false so yeah that's our tic-tac-toe game right there and so now let's actually
56:27
play this game so if name equals main if name equals main first we're going to make the x player equal to human player and assign it the letter x so actually at the very top we have to go back and we have to import human player and random computer player from our player file otherwise this game.pi file has no idea what's in player.hi but if we add in this like import at the top then we actually get our human player and we get our random computer player so for our x player we're going to
56:58
create a human player and we're going to initialize it with the letter x and then our o player we're going to make that our random computer player and assign that an o and then our game is gonna be tic-tac-toe let's just call that t and then we're gonna say t equals an instance of tic-tac-toe and we're gonna play tic-tac-toe so this is a game x player o player and then we're gonna set prank game equal to true right here all right so let's pull up a terminal
57:34
and let's play a game so python3 game dot pi alright so first i want to move to four because i want to be in the middle okay it's saying it's a tie that's weird but o makes a move to square seven let's try to go fix this it's a tie so if we go back into our loop we actually see that print it's a tie is still within this while loop and that's not right what we need to do is actually unindent it to make it fall outside the
58:04
while loop so it's only after there are no more available spots which means that there is a tie are we gonna print it's a tie first we're gonna go again to square four o is making a move to square five okay so i actually don't like how as soon as i said okay go to four the computers like print out its move immediately so what i'm gonna do is during this while loop so for every single iteration that we switch on and off i'm gonna add in a tiny pause and i can do that by calling time dot
58:37
sleep and let's just say 0.8 that's 0.8 seconds and at the very top we have to import time in order to make this work so here we're going to do a little tiny break to make things a little bit easier to read essentially and let's try writing this again so we make a move to square four o makes a move to square six and let's try the top left so zero o goes to three and we want that
59:12
bottom right so let's do nine oh eight we actually have to fix that text too though okay so we see that we win though all right uh let's go back into the code and it should actually be in player.pie here we're just going to edit this it's actually zero to eight okay save it so we were able to actually detect that it was an invalid square and that we had to try again so yeah that is our bare bones
59:44
tic-tac-toe implementation all right so we created a game of tic-tac-toe in python and we created a human player and we created a random computer player but can we do better can we make it so that the computer literally never loses maybe ties but never loses and the answer is yes so let's take a look at how we're going to do that
01:00:29
minimax is a decision making algorithm built off of a maximizer and minimizer concept basically you're trying to maximize your win while your opponent is trying to minimize their loss now in a game of tic-tac-toe we can step through each state and see how minimax might help us win and become victorious in minimax we are trying to find the best move to make we can determine this by trying out all the moves and figuring out which one is the most
01:01:01
optimal through something called the utility function which is basically a measurement of how valuable the final result in that tree is now let's take a look at an example using a partially completed game of tic-tac-toe so in this current board it's x's turn and obviously the goal is to maximize x since we want to win so the first step is to put down an x in every single possible potential move you'll notice that in the middle we
01:01:31
actually won the game because we formed three in a row now let's talk about this utility function for a little bit since we want to win we want our utility to be positive since this is a positive value to us now in addition i have this factor of 3 because i want to win in as little steps as possible so how i got this number is i took the remaining squares on the board and added one so that if you did win you still ended up with a positive value and not zero
01:02:02
and then i multiplied it by either plus one or minus one depending on who the winner was so for example if o had actually won in this situation with two squares left then i would have done negative one times two plus one three which is negative 3. so moving on from there we want to map out all the possible scenarios of gameplay so continuing this tree we have this layer and then this layer until the board is filled or until somebody
01:02:34
has one now let's add the utility function for all of these since o one on the left we're going to do negative one times one plus one since there's one square left which gives us negative two in the second one nobody wins and it's a draw so the value is just zero now on the right the first one we win again but in this case we don't have any empty squares
01:03:05
so we're just gonna multiply one by one and then on the right on the far right it's a draw again so we have a value of zero now that we have these utility values we can propagate them back up to find the most optimal path to take so at the very bottom level we have a maximizer function because it's x's turn there's actually no decision to be made in the bottom row because there's only one option one path to take in the second row it's o's turn and we
01:03:38
assume that o will be taking their most optimal path which means that we want to minimize the value that o has and in this case on the left hand side it's between negative 2 and 0 and since negative 2 is less than 0 the left gets assigned a value a utility value of negative two the middle is still three because there's no additional options after that and on the far right we're choosing between one and zero
01:04:10
and since zero is less than one the far right has a utility of zero in the next stage it's back to x's turn and we wanna maximize x between negative two three and zero obviously three is a maximum so we would choose that middle path so now we know what the most optimal solution is in order to win in the least number of steps possible all right so for our implementation we are going to create a genius computer player
01:04:45
and this of course is going to take player as a super class once again and here we're going to initialize it the same way we initialize our other two players so in our unbeatable computer player we also want to get move and get move is where all the magic is gonna happen at the very beginning if all the spaces are available let's just say grab a random spot and just go there so we're just randomly going to choose one otherwise all right so now we're going
01:05:16
to get the square based off of the minimax algorithm that we described so because of the nature of the algorithm and how it's recursive we're going to define a function minimax outside of our getmove function but we're going to call that from here so self.minimax and we're gonna need to pass in the game and the letter of the player so we know that we can win and not the other player so at the very end we return the square that was returned from our algorithm
01:05:46
and now let's define that algorithm here so to mini max and then self comma state comma player so the reason why i wanted to call this state and not game was because at every single iteration of minimax we pass in a representation a screenshot of the game in that state so i'm just calling it state it's just a variable name you could call it s you could call it game you call it whatever i'm going to call it state because in my head we're passing in states we're passing in
01:06:18
screenshots of the game all right so the max player is going to be yourself because you want to maximize your score so it's going to be self.letter and then other player is going to be other players so whatever the letter is not so first we want to check if the previous move is opener all right so when we have recursion we always need a base case and this base case is well at the very end of things where are we at so here we want to see
01:06:50
you know was there a winner in any of the states that we've passed in so we know that in our game we have a current winner so the current winner is equal to whatever other player is then we should return the position and the score because we need to keep track of both of these things for the algorithm to work so we're going to turn this into dictionary so the position is none and the score well so this is the formula that i was talking about earlier we're going to multiply 1 times the state dot
01:07:24
number of empty squares because we want to maximize the number of empty squares we want to get to a win as soon as possible plus one if the other player is the max player right otherwise we're gonna do the exact same thing but multiplied by negative one so else negative one and then state dot number of empty squares and then plus one okay so if there's no winner but if there are empty squares well that means that nobody's won so our
01:07:58
score is going to be neutral it's going to be zero and the position again will be none because we didn't move anywhere all right so these are our base cases all right so now here we're going to get into the algorithm so if the player is a max player then we're going to initialize a variable best and this is going to be a dictionary that's going to save the best position to move and the best score and because this player is going to be the max player yourself
01:08:28
you want to maximize that every single time step so you're comparing every single score to this score and you're trying to increment it so that means that we want to initialize it to the lowest possible score so at least one iteration will beat that score if we initialize it to negative infinity anything that's defined is going to beat that score if the player is not the max player then we want the position to still be initialized to none but the score we want initialize to infinity because we're trying to minimize
01:08:58
at every single point so we're trying to decrease that so we have to initialize to like the highest possible value so for every single possible move in the available moves we're gonna do a few things so the first step is we're gonna make a move and we're gonna try that spot so in step two we're gonna recurse using mini max to simulate a game after making that move so what happens like from there on if we make that move all right so step three we're going to have to undo that move so that we can try the next one in future
01:09:31
iteration right the fourth and final step is we need to update the dictionaries if necessary so if your score from that possible move actually beats the current best score then we want to replace that dictionary with whatever we get from that possible move all right so let's get into implementing these so first step one we want to call our state dot make move and this is going to be whatever possible move and the player that's making that move so player
01:10:02
right and then our simulated score is going to be well we just made that move so now we're going to pass the new state into mini max again and so here i'm going to call self.minimax state comma and then we're going to alternate players so it's going to be the other player step 3 we undo the move so at that possible move on the board we reset it to an empty space and then we set the state.currentwinner back to none so this is
01:10:33
undoing whatever move that we just did and the simulated score okay so remember that at the very end we return this position and then none right so we actually have to set what position we just moved to so here our simulated score position actually equals the possible move that we've just passed in otherwise this would kind of get messed up from like the recursion part all right so now in our fourth and final step we say if the player is a max player
01:11:03
if the sim score is actually greater than our best score then we replace this best dictionary with the sim score dictionary otherwise this means that your player is actually the min player and your sim score if it's less than your best score we again replace our best dictionary with the sim score because we've successfully gotten a lower score and so what we're doing is we're trying to maximize the max player but minimize the other player and at the very very end after we've
01:11:36
tried every single possible step then our best score this best dictionary will contain the best next possible move and the best score that can arise from it right it ends up returning a dictionary of the position and the score so in order to use that in our get move for our genius computer player we have to actually index for position and then that'll return the square the position where we're actually going to go oh actually this should be class sorry
01:12:07
that was a little mistake from my end all right and now instead of random computer player let's try playing against the genius computer player and we have to of course import that let's start a game we're gonna go to the middle square so four and we get this error so let's go back to our code and where did we call that where is this
01:12:39
error coming from so we actually see that we're missing an s right here small little mistake so let's try rewriting this after we fix that so four they go to zero let's go to the bottom left looks pretty good and that is square six so let's go to square six all right they go to square two so we gotta block them we gotta go to square one they kind of forced our hand one all right they go to seven so this
01:13:12
this is gonna be a tie game right so no matter where we go i mean yeah ta-da it's a tie all right let's try again and i'm going to show you how this algorithm is actually going to make a move cleverly to a spot where like it'll win so it's i'm just gonna show you it's gonna go there in the middle so let's go to the left and see what happens so that's square six the algorithm knows to take that bottom spot to win and here
01:13:44
what we actually can do is we can set this genius computer player to play against the random computer player and what i'm going to do is actually print turn print game to false and make this play a bunch of times so we're going to keep track of number of x wins owens and ties and then we're going to say like let's run this a thousand times remember that play passes back whoever wins right all right so i'm actually going to put this time dot sleep and print game
01:14:15
otherwise it's going to be kind of it's going to slow it down unnecessarily so at the very end our result is going to be whatever play spits out because that's the winner so if the result is x then we're going to increment x wins plus equals one if the result is o then we're going to increment o wins by one and then now if it's none so that means x is a win o doesn't win then we're
01:14:45
going to increment ties by one and then at the very end we're gonna print like who won what right so after a thousand iterations we see x wins x wins we see o wins o wins and then we see ty's number of ties all right let's try running this 1000 times this actually takes a while to run so let's do a little bit of magic
01:15:20
called video editing and bam we see that after 1000 iterations we see zero x wins 793 o wins and 207 ties and you can run this with the human with the random computer player as x or o but if you are playing against a genius computer player you will realize that it never loses it only wins and it only ties but it never loses you can run this with like a million iterations and it will not lose
01:15:55
before we implement binary search let's actually understand what binary search is binary search algorithm is a divide and conquer algorithm which actually helps you search an ordered list faster than just scanning every single element and asking hey is this it is this it is this it and what i mean by divide and conquer is that binary search essentially works like this so assume that we have some list of ordered elements from least to greatest and we're trying to see if this target is in the list and if it is
01:16:27
then return the index of where it is so essentially we're searching this list for this target so what we can do in binary search is we can go to that middle element of this list and we can ask hey is the target equal less than or greater than this middle element if it's equal to then well we've found it right but if it's less than then we know that it actually has to be on the left side of that element and we can completely disregard searching anything greater than that element so we can completely disregard
01:16:59
the right hand side of that list and now vice versa if it's greater than that middle element then we only have to search the right half of the list and now again we can reiterate on that one section so we divide and then we conquer that one section so in that left side let's say that our target is smaller than so let's say our target is 7 and 7 is less than 15. so on that left hand side what we can do is we can redo the exact same thing take the
01:17:29
middle of that left-hand side our target is it less than greater than or equal to if it's equal to we're done we've found it if it's less than again we look at the left-hand side if it's greater than then we look at the right-hand side but up to that middle point where we've already checked because we're limiting ourselves to that left hand side of the array let's say that our target is actually greater than this next middle and so then we look again on that right hand side of the array and you can imagine how with a really really big array we keep dividing it in half every single
01:18:01
time because eventually it'll either be the midpoint of a bigger array or we'll come down to like a sub array of like size one and then we found our element there so in this project i'm actually going to prove that binary search is faster than naive search and by naive search i just mean you're iterating through the entire list and you're asking hey does this value equal our target what about here here here here here and so on and so you're basically asking every single element
01:18:31
until you hit whatever your target is so in naive search we're scanning the entire list asking if it's equal to the target if it is then we return the index if not then we return negative one so let's define naive search we're going to give it a list l and a target so for i and range length l so for every single index if l at that index is the target then we return that index otherwise we've gone through the entire list nothing's there we return negative one
01:19:03
for example our l could be 1 3 10 12 right and if 10 is our target then we're saying okay for the first i so if i equal zero our element number is one that does not equal the target keep going so then three this three equal target no keep going all right so now we're at index two okay well ten equals the target so we return two and if it's not in there then we end up returning negative one at the very end
01:19:35
okay so then binary search uses again divide and conquer so we will leverage the fact that our list is sorted in order to make our search faster so let's define binary search and then give it again a list l and a target all right so here i'm going to provide another example and i'm actually going to add one more element in here so it's one longer than our previous example so 1 3 5 10 12 and let's say we're searching for 10 again and so here we're just saying okay it
01:20:11
should return 3 index 3 because 10 is at index 3. so the first thing that we have to do is we have to find our midpoint so our midpoint is going to be the length of this list and then divided by 2 but round it down and this double dash here this means this means how many times does two go into length right so that's going to give us approximately the index of the midpoint so now if l at this midpoint so if this
01:20:41
list at the midpoint is equal to the target then we can return that midpoint right there because that's our index now if the target is less than the value at that midpoint so our target's 10 our value is 5. so this is comparing 10 less than 5 right which is not true but if it were again this is saying like chop off half of the list and iterate over only one section of it so we're going to recurse so we're going to use binary search again on that one section that we're given
01:21:13
which here would be one comma three if this value if this statement had been true so again we're going to have to pass in some list and i'm just going to leave this as l for now we have to divide it we're not dividing anything right now but i'll get back to it so then in the else statement well this means that the target has to be greater than whatever value's at that midpoint so we only check what's to the right of it right so then we do another binary search all right now these two i told
01:21:44
you guys i would get back to the fact that we're gonna divide it in a bit and what we can do is we could theoretically say okay actually just pass in that half of the array so we could index l so that it's the left or right hand side but then we just have to add the index back in another way is that we can add in low and high into our binary search and these are going to be the lowest indices to the highest indices that we search and then here when we recurse we can say
01:22:15
the low is equal to the current low but the new high is going to be the midpoint minus and then for the other side we can say the low is now going to be well the next one after the midpoint all the way until whatever the original high value was and again low and high are indices so these are all the indices in our list that we can check and these are just bounds on the indices all right so first if low is none then let's set low to zero because we want
01:22:47
low to be the lowest possible index that we can check and then high if high is none high is going to be the highest possible index that we can check which is length l minus 1. all right and then for our midpoint instead of just the length of l we're going to change this to low plus high because remember this is the lowest indices plus the highest indices so the average of those two would be whatever index is in the middle so that's our midpoint how do we know that our target's not even in the list what we can do is we can say all right down here every single time
01:23:18
our target is to the left of the midpoint we're actually subtracting one from the high and then every single time our target is to the right of the midpoint we're adding one to the midpoint for the low so if the high bound is ever less than the low bound that should never happen theoretically if this were iterated properly the only time is when it can't find it so the target is not in the list then we return negative one so that's our case of you know it's it's not in this list
01:23:50
all right so if name equals main then let's create a list 1 3 5 10 12. let's make the target equal to 10 and then let's print naive search of this list and then the target and then also binary search for that all right opening terminal let's run this script and we see both of them return three all right now let's do a little bit of timing analysis to show you guys that it actually works to not check the entire list
01:24:31
all right so let's set length to ten thousand and so here we're going to just build a random sorted list of length ten thousand let's say the values in our sorted list let's initialize it to a set first and then while the length of this set is less than length well we're going to add some random integer and just to have bounds on this let's do something like negative 3 times the length of the list and then all the way till three times the length of the list so that gives us a range of like
01:25:00
negative 30 000 to 30 000 for our algorithm to just choose a random number and add it into this list all right so then after this is done then we're going to say okay make the sorted list into a list and then sort it so that's what sorted is so and then we're going to reassign this to the variable sorted list and then we're going to import time because well we're going to want to time how long it takes for each of those and how we're going to do that is we're going to say okay start equals time.time
01:25:33
so that gets the time right now and then we're going to call naive search on the sorted list and get some target and let's actually say let's iterate through every single item in this list and try to find that item in the list so for target in the sorted list so that means we're going through the entire sorted list and making everything the target and then running naive search on that one target so we're basically running naive search ten thousand times here
01:26:05
and the end time is going to be again time.time at that spot and so then the naive search time is actually just the end time minus the start time and so we can actually do this per iteration if we divide it by length so for each iteration on average it's going to be n minus start and then divide that by length number of seconds and again we're going to do the exact same thing for binary search
01:26:36
but make it binary search so let's run that okay we see that naive search takes approximately like .443 like milliseconds so that's 443 microseconds whereas binary search i mean it takes 6.8 microseconds so let's compare that that's like 6.8 compared to 400 something for a naive search so yeah basically if you ever have to search a sorted list
01:27:12
never search every single item i'm gonna be showing you guys how to build a command line version of minesweeper that looks something like [Music] this [Music] [Applause] we're going to be using recursion and classes to build our game now before we get started i just wanted to say that i'm building a very bare bones command line version of this game
01:27:50
because i believe that when you're learning how to code if you actually want to learn how to translate your ideas and algorithms into python the bulk of that is going to be in actually implementing the game not figuring out how the ui works i think that the ui while it's important it is somewhat secondary to actually learning the coding process and the algorithmic process that's involved with building these games let's start off by defining the play function
01:28:20
so here our goal of this function is to play the game so we're going to find play pass in a dimension size which is going to be the size of the board and the number of bombs on the board all right so in step one we're going to create the board and plant the bombs in step two we're going to show the user the board and ask them where they want to go and now step three well if the location is a bomb then we show the game over message because they just dug where there was a bomb and but if the
01:28:53
location is not a bomb then we dig recursively until each square is at least next to a bomb right so you think about how minesweeper works if you dig somewhere and it's empty and everything around it is empty then you keep digging until you get to a number and that number represents that that square is next to a bomb and then in step four we repeat steps two and three until there are no more places to dig and then we've achieved victory all right so
01:29:25
right now we're just going to say pass because we'll get back to this function okay so let's take advantage of our object-oriented programming tools that we have in python and let's create a board object to represent the mind sweeper game so this is where we can say create a new object or dig here or render this game for this object let's define a class called board and here we're going to initialize it with the dimension size
01:29:59
and the number of boards so let's keep track of these parameters because they're going to be helpful later on so let's assign self.dimension size dim size to the dimension size that was passed in and then sell that number of bombs to the number of bombs that was passed in and then we're going to create the board but let's get back to that and at the very end we're going to initialize a set to keep track of which locations we've uncovered which locations we've dug
01:30:31
in where the user has gone so self.dog is going to be an empty set okay and then let's create the board so let's actually use a helper function so self.board equals self dot make new boards and here we're going to plant the bombs too so let's define that all right so we're going to find make new board pass in self and basically this is going to construct a new board based on the dimension size and the number of bombs that we pass in and there are a bunch of
01:31:04
different representations that we can use so that can be like a list of lists where each sub list is just a row of this board so here we're going to generate a new board this board is going to equal it's going to be none and then repeat that dimension size number of times because that's how long we want this list to be and then we're going to have dimension size number of these lists so that we can get a square board and so this creates an array that looks something like this it's just going to be a board where it's
01:31:34
not none etc for our whatever dimension size that we define it so then next we have to plant the bombs so here we're going to say bombs planted equals zero we're just going to use a while loop and we can say while bombs planted is less than self that number bombs we can pick a random location for the bomb right so let's actually go up here and let's import random and now for the location we're going to do random dot rand and somewhere between
01:32:06
zero and self.dimension size squared minus one and so you can think about this logic as like we're literally assigning a number from zero you know to the number of spots on the board and we're assigning each spot on the board its own unique id and then this random dot ran in is returning a random integer n such that it's between the bounds a and b so a would be zero b would be the largest id in that list and then here we want to actually get the row and the column of that id that we've
01:32:38
chosen from this random selector so we're gonna do the location and then this double slash self.dim size and what this is gonna do it's gonna say how many times does my dimension size go into whatever location that i've chosen that's going to be the row that we're indexing in and then now once we have the row how do we know which column we have to divide by the dimension size and then whatever index is left over that's going to be how far into that
01:33:08
list we have to index in order to find the column so once we have the row index and the column index we can index into the board and then we can say if bored and then row column we're going to index into that specific row column location on the board if it equals a bomb so the star is what we're going to use to represent the bombs then this means that we've actually planted a bomb there already so we're not going to increase bombs planted right instead we're just going to keep going
01:33:39
and this is actually the reason why i'm using a while loop and not a for loop because in a for loop you know every single time we skip or like continue we're still counting that as an iteration but here i only want to increment when i actually get something that's not a bomb yet and then i plant the bomb so yeah that's why i'm using a counter and that's why i'm saying hey check if this is a bomb if it is keep going if not then we're actually going to plant the bomb and then we're going to increment this counter bombs planted
01:34:11
and at the very end i'm going to return the board all right so that is making our new board we're going to plant the bombs right there all right so what other information is useful well we want to know at each spot on this board how many bombs are around that spot that's going to help us when we choose where to actually dig right when the user inputs something well how do we know where you know whether or
01:34:42
not we should keep digging around it and yes we can implement a check at each point where we say like oh if we dig here we're going to check all its neighbors and then we're going to dig again and you know so on but we could also just assign values to every single space on the board that represents how many bombs are in the neighboring spaces so let's call that assign values to board so here let's define assigned values to board and pass in self
01:35:16
all right so now that we have the bombs planted we're assigning a number zero through eight for all the empty spaces that don't have bombs and this is basically representing how many neighboring bombs are there we can pre-compute these and it'll save some effort checking what's around that square later on basically we want to check every row and every column so for r and range self dot dimension size for c and range self.dimension size this is going to be the row index and the column index
01:35:46
so if the item at the board so if at those indices on the board it's a bomb we're going to continue right because we don't want to actually override any of the bombs that we've planted but if it's not then we pass this if statement and then we say okay for this location on the board let's create a new function called get num neighboring bombs pass in the row index and the column index and then this function is going to give us the number of bombs that is surrounding that
01:36:19
row comma column all right so let's define this define get number of neighboring bombs we're passing in the rogue and the column like if you're confused of why i have r comma c up top and then row comma call these are just variable names you just have to make sure that they match where you're actually calling them so for example when i call the function i'm passing it r comma c because i've defined r and i've defined c in my for loop so those are variables that i've defined and now when i create this new function i can
01:36:51
call the parameters that get passed in whatever i want right and so here the r would correspond to the row and the c would correspond to the column so now let's iterate through each of the neighboring positions and sum up the number of bombs so here i've kind of imported a list of all the neighboring positions you can see the top left is row minus one column minus one and top middle is row minus one column you know and so on and so we're going to
01:37:22
check all of these and we have to make sure that we don't go out of bounds so that's just a little reminder okay so first we're going to initialize the number of neighboring bombs just to a variable set it to zero this is going to be our counter and then we're going to say for r and range row minus one row plus one and then keep in mind that due to the nature of the range function in python we have to add a plus one to the end and then same thing for the column for c and range column minus one
01:37:50
column plus one plus one um that should be a plus all right so basically what we're doing here is for the current row that we're at we're checking below and above and then for the current column we're checking to left and to the right and so when we sum up all these combinations we get every little piece of the 3x3 grid and then we can say if r equals the current row that we've passed in and if c equals call the column that we've passed in this is basically saying like this is our original location we don't actually have to check this
01:38:27
so we continue but if it's not if self.board at rc equals a star so that means that there is a bomb at that location that means that we have a neighboring bomb right and so we can increment number of neighboring bombs by one and then at the very end after we've gone through all the neighbors we're just gonna return the total number of neighboring bombs now we have to go back up here and we have to make sure that we don't actually go out of bounds right because row what if row is at zero what if we're checking the first row row minus one is gonna be negative one that's out of
01:38:59
bounds here we're gonna add a max statement just to make sure that you know row minus one if it ever goes past negative one we're gonna take zero every single time we go below that zero bound and then for the upper bound we're gonna do the same thing we're gonna take the min of row plus one and then self dot dimension size minus one because that is the maximum index that we can go right and then of course we're going to use the exact same logic for the columns just like this solve some spacing stuff and so yeah now
01:39:40
we've got our bounds checking and we can return the number of neighboring bombs all right let's go back to our play function now so step one is creating the board and planning the bombs so what we can do is we can say the board equals an instance of this board class and then we're going to pass in the dimension size and the number of bombs that we've passed into this play function and this is going to automatically you know go through that initialization function and it's gonna initialize the
01:40:10
board and plant the bombs and create an empty set for doug and so on all right so now part two we're gonna show the use of the board and ask where they want to dig and then we're going to check if the location is a bomb is not a bomb we're going to dig recursively so let's actually go back and let's implement some of these functions so we have them you know handy when we need them so let's define a function called dig within the board class and we can dig at a certain row and column index
01:40:42
so we're digging at whatever location the user has specified and let's return true if it's a successful dig and then a false if we've actually dug a bomb it's game over and we've lost so there are a few scenarios here right either you know we can dig somewhere and we hit a bomb and then it's game over we can dig at a location with neighboring bombs and then you know we finish the dig because we've uncovered a number that's not zero but we might also be digging at a spot where there
01:41:13
are no neighboring bonds and in that case we want to dig its neighbors until we actually get somewhere where there are neighboring bombs so the first thing that we want to do when we dig at a row comma column is we want to add a tuple to self.doug to make sure that we're keeping track of where we've actually dug and then we're going to check the board at that row and column so in our first scenario if it's a bomb we return false if there's a bomb dog now if we check that space and you know
01:41:47
it's a number that's greater than zero that means that we've dug out a location with neighboring bombs and we finished the dig so we just returned true because we did not dig a bomb so now if at that row and column on the board it's not a bomb it's not a number greater than zero it means that that spot is equal to zero right so here we're going to use the same logic from get number of neighboring bombs where we're checking for the neighbors and let's paste that down here so here we're going to check r and c for
01:42:19
all the neighbors and so if r comma c is in self.dog then we continue because this is basically saying you know don't dig where you've already dug that's pointless right but after that if it's not then we dig at that location so we pass an r comma c into this dig function again and we continue through all of this there shouldn't be a way where we ever get to a bomb right because we should
01:42:50
always be stopping at some square right before a bomb so at the very end we're gonna return true so i'm just gonna add one more thing to this minesweeper game this underscore underscore string underscore underscore and so this is a magic function where if you call print on this board it's going to you know it's going to print out whatever the string function returns and so here what we're going to want to do is we're going to want to return a string that shows a board to
01:43:24
the player and i'm going to go over part of this function but part of it was kind of just like like if you inspect the code you'll be able to tell what we're kind of doing okay so first we're gonna create a new array that represents what the user should see so let's call this visible board so visible board is going to be well first let's just create an empty board just as we did above so that's gonna be our list and list and it's going to be a sub list of size dimension size and we're going to have
01:43:55
dimension size number of those lists so now for every single row and for every single column we're going to use this for loop again um if that row comma column is in self.doug that means the user has dug at that spot so visibleboard at that row and that column is going to be whatever the actual board value is but if it's not dug already then this is actually just going to be a space because the user shouldn't be able to see what's
01:44:27
at that location yet they haven't dug there and we're going to put this entire board representation in a string now what you can do is you can just honestly return like a string.join and then just this visible board in this code i'm going to make it a little bit cleaner and this is all that this stuff here is doing it's just some formatting code you can look through it if you want but i'm just telling you right now that it's just completely
01:44:58
formatting to make it look prettier and to make it print out nice and honestly i believe that knowing how to implement minesweeper is a lot more important to learn than learning how to print out a representation of the game okay so now if we look at steps two three and four well step four is basically repeating steps two and three until there are no more places to dig so that kind of sounds like a while loop to me right and here what we're gonna do is we're gonna say well while the length of
01:45:30
board dug so all the places that you've dug remember that this is a set so there are no duplicates while the length of this set is less than the board dot dimension size squared because that's how many spaces total there are on the board minus number of bombs then we're going to allow the user to play because it means that they still have empty spaces on the board where they can dig that are not bombs so the first thing we're going to do is we're going to print the board and we're going to ask the user where would you like to dig and we're going to input this
01:46:01
as row comma call so something like 0 comma 3. and now here i'm going to use a regex split so that's what re dot split is it's saying input where would you like to dig this is going to return a string and we're going to split that string by this reg x and so this comma is going to say okay detect any commas and then this parentheses slash slash s well this is saying any white space so any spaces that you see and the star at the end is gonna say
01:46:35
zero or more of those so essentially this is saying match any part of this string that matches you know just a comma or a comma space or a comma space space whatever the user types we're going to split that so then we can handle something like 0 0 zero comma space zero or zero comma space to say space zero right and here we have to import r e so let's go back to the top and import r e so that we can split
01:47:06
our string okay so now that we've split our input we now know what row and column the user is trying to dig so we can assign row and column to the user input at zero and the user input at negative one the reason why we use negative one is sometimes it's r u dot split it has some fluff on the inside of this list and so if we know the row and the column are at the beginning and the end then why not just take the first and the last item in this list and i'm going to use int because we want
01:47:37
these to be integers so now let's do some bounce checking if row is either less than 0 or greater than board.dimension size or if column is less than 0 greater than or equal to the dimension size we're out of bounds so here we're going to print invalid location try again and we're going to continue so that we repeat this loop over again until the user inputs a valid row and column but if the user did input something valid then we did get that location
01:48:08
so we can call board.dig at row comma call and so now we've already implemented this part so we don't have to actually worry about the mechanisms of actually digging we know that board.dig is going to return true if we've dug successfully and false if not and so we can assign a variable called safe whether or not our dig was safe so whether or not we've uncovered a bomb or not right so at the very beginning we're actually going to say safe is true because at the very beginning we haven't
01:48:39
dug anything you know we're safe we have all of our lives and so if not safe anymore well this means that we've dug a bomb and that's bad we're gonna call break because this means game over we shouldn't be continuing this loop anymore we shouldn't be allowing the user to dig so we're going to break out of that while loop and now at the very end there's two ways to exit this while loop right either you've won and there's no more
01:49:10
spaces on the board where you can dig or you've dug a bomb nothing safe anymore and yeah rip so let's check which one if we're still safe this means that we've just run out of spaces to dig we've dug every single possible spot to dig and so we've actually won let's prank congratulations you are victorious all right but on the other hand if we're not safe that means
01:49:42
that we've dug a bomb and we can print sorry game over and here we can actually reveal the whole board so we're going to assign board to every single possible value of r comma c on this board so this double for loop in this list comprehension here is just saying take every single possible r comma c value of this board and put it in this list at the very end we're going to print the board and so this is going to reveal every single possible spot now let's call the play function to
01:50:21
actually play the game and we're going to put this in a name equals main if statement because this is just good practice it's basically saying like if you have a massive project but you only want to run this file the stuff underneath name equals main will only run if you type in python3 mindsweeper.pi if you have a bunch of imports from a bunch of other files it's not going to run any of the code under name equals main in those files you're only running what's under name equals main in that one file alright so let's play the game first
01:50:52
let's dig at i don't know zero zero so here you see that we've dug at zero zero it was zero so that means that you know there were no bombs nearby and so we kept digging until you know there were some bombs nearby so for our next spot let's just do four comma six and then let's try that bottom right corner so nine comma nine and you'll see that this actually dug a lot so it recursively dug everything that you see that's zero
01:51:21
it dug until it hit some spot that was not zero and if we look at this spot right here three comma seven well this is actually you know very clearly a bomb right so let's dig there on purpose so we dug through seven and it says sorry game over and it actually reveals that yes this was a bomb and this was the entire map of our game to begin with there you have it a command line version
01:51:52
of minesweeper the next project is a sudoku solver in this tutorial you'll be able to see how we can use recursion to solve any valid sudoku puzzle okay so the first thing that we want to do is we want to define our function solve sudoku and we want to pass in our puzzle basically this function is going to solve sudoku using a backtracking technique so our puzzle that we pass in is a list of lists where each inner list is actually a row in the sudoku puzzle so basically this represents the 9x9
01:52:28
puzzle and we're turning whether or not the solution exists but in this code remember how lists are mutable so we're actually mutating this puzzle to be the solution if the solution exists so the first step is i'm actually gonna see where on the puzzle i can go so as a human when we're playing sudoku we typically go from where we have the most information whether it's the column that's most filled out or the row or the little tiny three by three box but because we have a computer we don't
01:52:59
have to do that what we can do is we can assign a number to any open space on the board and then we can try essentially every single combination as long as it's valid and when we see that it's not valid we can actually go back and say oh let's not try three let's try four instead and if you think about the entire puzzle you can essentially come up with like every single combination oh it doesn't work from there okay well let's take a step back and try all the combinations there if none of those work then we take another
01:53:31
step back and try all the combinations there and so on and our computer is actually powerful enough to do that so that's the technique that we're going to use here so our first step is actually to choose somewhere on the board to make a guess in order to do this i'm going to create a helper function called find next empty and pass in the puzzle so i can find the open spaces on the board so here i'm going to define find next empty pass in puzzle and essentially this function is going
01:54:01
to find the next row column on the puzzle that's not filled yet and in our puzzle we're representing any open spaces with negative ones so we basically want to return the next space that equals negative one so we're going to return the index of the row so that's if this is a list of lists the first index that we return is the location in that first list that our empty space is at the second value that we're returning is within that row which index is it at
01:54:31
and then of course there might be a situation where our entire board is filled and there's no empty spaces left in that case we're gonna return a tuple none comma none so keep in mind that we're actually zero indexing so we're starting from zero and our last index is eight so essentially what i can do is i can just go in order i can say hey check each row and then check each column and whatever the first empty space you get i'm just going to return the row comma column
01:55:02
value of that so here i can do for each row in range nine so i'm iterating through my nine rows and then i can say for that row for each column value in range nine so that's my zero through eight if the puzzle and then here's how we index we pick out the row and then within that row we use c to index again and get the column so here this double indexing basically is returning the item in the arthro and the seath column and then if that equals negative
01:55:33
one then basically we return that rc otherwise we return none comma none if we've iterated through this entire puzzle and nothing equals negative one then that means that there's no spaces in the puzzle left so we can return none common none okay so then the second step from there is well if there's nowhere left we're going to be implementing some validation checks of like whether our guess is actually valid or not and so if we filled out this entire puzzle then that means
01:56:07
we're actually done so here i'm going to say if row is none so remember that above we return none comma none and then that first nun gets assigned to row the second nun gets assigned to column so we only have to check one of them now if row is none then we can return true because we've actually solved our puzzle but if we haven't then we can keep going all right step two is basically if there's a place to put our guess
01:56:39
then we want to come up with a guess between 1 and 9 and we want to actually try all of them until we find a combination that works so i'm going to say for guess in range 1 comma 10. we start the next step step three which is checking if this is a valid guess okay so here i'm going to use another helper function is valid and i'm going to pass in the puzzle guess row and column because those those are the
01:57:14
key pieces of information that we need in order to determine whether or not this guess at this row and column is valid in our puzzle so those are the four parameters that we need and here i'm going to define the function is valid and this basically figures out whether the guess at that row or column of the puzzle is valid and so if there's no conflicts then we consider it valid and then we return true we return false if it's not
01:57:45
so now we have to follow sudoku rules if our guess equals anything that exists in that row or the column already or even the little three by three matrix that it's in then it's not valid so let's actually start with the row because that one's the easy one right every single list within our puzzle represents a row so if we have the row index then we can just say that the row values are equal to the puzzle index at the row so if our guess is in row values then we can return false all right now
01:58:16
the columns the columns are a little bit trickier because we actually vary which row we're indexing into but we index at the same spot within each row so what we can do is we can create a new list called column values and we can say for each row i mean i can say for i enrage nine so that will go through all the rows i'm going to append the value at puzzle at the i throw at the call column and so another way to
01:58:48
write this actually is using a list comprehension where i can say take puzzle and then index into i and then within that row index into call and then do that for i in range nine and then that's essentially going to build the exact same list so then if the guess is in those values then we return false because it means that it's in our column and then now this the little three by three square matrix so this part's a little bit trickier because we actually have to figure out
01:59:19
where in the 3x3 grid we are and so to do this what we're going to do is we're going to find the starting index of the row of that by three matrix and then we're gonna find the starting column index of that three by three matrix and then we're gonna say for each row for each of the columns within that three we're gonna iterate so what we can do in order to find this is actually take the row index and divide it by three
01:59:52
but throw away the remainder what is like for example if i have 1 divided by 3 that comes out to 0.333 so the base or whatever you want to call it is 0. and then 5 divided by 3 well the remainder is 2 but 3 goes into 5 one time so i'm going to return one so then i can take that and i can figure out if it's in the first set of three rows the second set of three rows or the third set of three rows and then of course in order to get like the actual index of that i have to
02:00:22
multiply that value by three and then it's the exact same logic for the columns so when i get row start i'm trying to get the start value of these chunks right but then when i'm getting the column i'm getting the start value of these chunks maybe it's the other way around on youtube when i have both of the starts i can say hey now we're going to iterate through this so i can say for r and range row start comma row start plus 3 because we want to iterate through the three rows
02:00:53
for c in range call start to call start plus three because we want to iterate through three columns if the value at the puzzle equals our guess so that means our guess is already in this three by three matrix so we just have to return false and now at the very end if we've passed all of these checks and we haven't returned false yet that means that well it is valid and we can return true all right so let's close those functions okay now back to our sudoku code
02:01:30
so if is valid is true then we want to actually place that guess on the puzzle at that row comma column so what we can do is we can say puzzle index at the row index at the column is now equal to our guess so we're actually mutating this puzzle array so now in step four we're going to recursively call our function because if i guess one number then that number is actually mutated in my list of lists and i can pass that in
02:02:04
as my puzzle and then the next value becomes mutated and then so on until we reach the very end so that's essentially what we're doing here we're just solving this entire thing with this new guess in our array so if that comes out as true then we know that we've actually solved our sudoku puzzle so we can return true but of course there's also the case where where this is valid check might not be valid and there's also this case of well what if
02:02:38
we didn't solve the sudoku when we tried that guess in the row and column so then in this case we actually need to backtrack we need to say hey so this guess was wrong let's reset it and move on to the next guess so here i'm going to say puzzle row call equals negative one because we didn't successfully place anything there so we're essentially just resetting the value at this row and column and now you can imagine this
02:03:11
for loop is going to go over you know all the possible values one two three four five six seven eight nine for every single empty spot along this entire puzzle right so that means we're literally trying every single possible combination for the sudoku so in our last step if we've tried every single combination possible and none of them work then that means that we actually can't find a solution so this puzzle is unsolvable and then we're going to return false
02:03:43
all right so let's actually test this to prove that it works okay so python 3 sudoku.pi we see that it comes out as true and this is our board so let's try resizing this to see if we can like actually view this as a board okay there we go let's do a couple of checks just to make sure that like our solution is actually true so in this first box up here let's do one two three four five six seven eight nine all right and then this column we have one two
02:04:12
three four five six seven eight nine in this row one two three four five six seven eight nine okay so that's pretty convincing that like this is actually a good solution okay so a couple of notes about my implementation recursion is confusing recursion kind of makes your brain hurt i think it might be better understood this way think about solve sudoku as a black box it should be able to mutate the input puzzle so that it's a
02:04:44
solution if it's a valid input puzzle now if it's not a valid input puzzle well then it should be able to identify that because we've tried every single combination for that puzzle so when we make a new guess we can pass this new guess as a puzzle into our solve sudoku function and if it's solvable then we know that our guess was the correct guess and we've actually reached a solution now if it's not solvable well then we know that that guess that we passed in
02:05:15
it's not a solvable sudoku solution so we can say okay that wasn't the right guess so now let's move on to the next one and this is how we kind of go through this entire puzzle and mutate the sudoku array that we originally pass in to be the correct answer i hope that clears things up for you guys this next project is going to deal with editing images in python i've prepared some starter code if you go to this link down below the one that's for pi photoshop
02:05:50
you can click this download code you can either download the zip file or you can get clone it if you know how to do that and let's take a look at what's in this code so here i've prepared for you a lake and a city image and so we're going to actually be editing these images and doing like cool things on those using python so in the png.pi file this is some code that johann
02:06:20
roshol put together and i just copy and pasted this from online essentially what it is is it's a png reader and a writer and what that means is well the writer is a png encoder in python and then the reader is a png decoder in python so it takes a png image and it decodes it into like a python array and vice versa for the writer it takes a python array and it writes it to a png file pretty
02:06:51
cool all right so now in this image class this is some code that i've prepared for you and we can see in this initialization you can either initialize it with x pixels y pixels and num channels and that will initialize to an empty array of zeros or you can import a file and this image will represent whatever file that you've imported so here we have the input path and the output path these are just the folders for the inputs and the outputs and
02:07:22
here we have a checker to see if the user has actually passed in x pixels y pixels and num channels so if it has we assign those values and then we create an empty array essentially by the way number of channels just means like for example typically when we work with images we work with rgb channels so that's red green and blue and so that's three channels that's what we're going to be using today and then x pixels and y pixels will describe the size the actual
02:07:53
physical size of the image so here we're initializing these to all zero and this is going to be a numpy array of the size x pixels y pixels num channels so you can think of this command as kind of just creating a three-dimensional matrix with dimensions x pixels y pixels num channels and it's initialized to all zero that's essentially what self dot array is initialized to when you pass in x pixels y pixels and num channels so if there is a file name then we
02:08:24
actually read that image from this helper function read image and set that to the array and then x pixels y pixels and num channels will set to that array dot shape at the very end we're going to add this else statement because if the user hasn't passed in x y and num channels or if they haven't passed in file name then we're actually going to raise a value error saying you have to input one of those options okay so let's go over the read image function so read image you have to
02:08:55
pass in a file name and this gamma you don't have to worry too much about that it's just a way to encode and decode it so that your operations are not exactly linear and so here i'm using png reader this is from the png file and i'm passing in the file name i'm gonna read it as a float and then here we're just gonna do a bunch of like resizing things i mean i've given you guys these functions for a reason it's because i don't think that they're critical in understanding how the actual
02:09:25
photo manipulation works so then in this right image so this function call will write whatever this image represents to a png file and we're clipping it to between zero and one the reason for that is because when we transform it back into the output file we're going to scale everything from 0 to 255 and so we're going to do a little bit of reshaping and write it out to the output file using the png
02:09:56
writer and we're going to resize this array because we did a little bit of reshaping but we want to keep it in the same representation right we don't want to actually mutate our representation of the array so down here we're just going to do a quick test to see that this like import and export works so we're going to call image equals image dot file name and let's use the link and all we're going to do is we're going to write to the output file we're going to write image test.png and what we should see
02:10:27
is that test.png should be identical to lake.png because we haven't manipulated the array at all so let's try that okay so test.png this is the same image and so where the bulk of our code is going to be is in this transform.pi file we're going to implement a couple of things here the first thing that we're going to implement
02:10:58
is adjust brightness so how do we adjust the brightness here basically when we adjust the brightness we want to scale each value of the pixel by some amount factor that's a value greater than zero it's basically how much you want to brighten or darken the image by if the factor is less than one then we're darkening and if it's greater than one then we're brightening so first we have to figure out how big exactly this image is so that we can iterate through each pixel and so first we get
02:11:30
image.array.shape because remember that we've stored our values in self.array for that image all right so basically we're getting the x y pixels and then we're getting the channels okay and then basically we're gonna make an empty image so that we don't actually mutate this one that we're passing in so this new image is going to be x pixels equals
02:12:00
x pixels y pixels equals y pixels and then num channels equals num channels so it's gonna be the exact same size of the array that we pass in but now we're just gonna be mutating this new image so we don't change the original one this is maybe the most intuitive way to do this it's non-vectorized if you don't know what that means don't worry about it i'll show you in a bit but essentially we're going to iterate for every single pixel and x pixels for every single y pixel
02:12:31
and then for every single value of the channel so literally you can imagine this 3d matrix and you're iterating through each individual value and then for that value well we have to adjust the brightness by some factor so we're going to say new image and we're going to take the array and then we're going to index into whatever position that we're currently at so x y c so at index x y c we're going to set this equal
02:13:01
to our original image that array at x y c so that corresponding pixel and then multiply that by the factor that the user has input and then at the very end we're just going to return the new image so let's actually try this first see that it works here at the very bottom i've provided already a little bit of code that tells you all right load the lake and load the city so let's lighten the lake for example
02:13:32
so let's say brand m equals adjust brightness lake and then some factor greater than one so let's just do 1.7 and then we're going to write this image to brightoned.png so let's try that so python3 transform.pi and we get this image that's slightly brighter we can compare this to the lake if we move these side by side you'll notice that the brighten one is like slightly brighter so let's actually try also darkening so
02:14:07
darken image equals adjust brightness and then let's make the factor 0.3 and let's save this as darkened and now writing that again alright we get the darkened image here so we can tell that this is darkened from our original image and let's compare these side by side again right so the darkened image does look darker okay so i mentioned earlier that this is a non-vectorized way to do it and this is because
02:14:47
this is the most intuitive like this is behind the scenes if you are brightening or darkening something you have to adjust every single pixel value and increase or decrease it all right so one faster way to do it is the vectorized version so we said before that these are numpy arrays but the strength of this such array is that okay it's numpy but i've always read it as numpy but basically the strength of this array is that you can vectorize these operations so if you want to add a
02:15:19
constant if you want to multiply by some scaling factor you can directly just call that array and then times that factor it's significantly faster than iterating through using a for loop and we can see that this does the exact same thing if we just let it run on the darkened image so let's do darken image two let's call the transform all right we get the starkit image two and this looks the exact same as our darkened image
02:15:50
let's move on okay we're going to adjust the contrast now so when we adjust the contrast we're going to be doing the same thing where we want to create a new image copies so that we can put new values in without modifying the original image we're going to be repeating this for x y and c thing because even if you can vectorize things i i like this way of just showing you guys what's like actually going on here we're going to index at
02:16:21
xyz that position again in the array of the new image so what adjust contrast does is it adjusts the contrast by increasing the difference from the user-defined midpoint by some amount factor so essentially if your point is above the factor then you take that difference you scale it by factor and then you add back whatever the midpoint was same thing for the other side so basically what you're doing is given this midpoint you're making the difference from the midpoint greater so we're going to take the
02:16:53
image.array and we're going to take the value at x comma y comma c subtract out the midpoint and then scale that by the factor factor and then we're going to add the midpoint back in all right and then of course we return that new image and just to show you guys what the vectorized version would look like it's just new image.array equals the image.array minus mid which is a constant so it's taking that entire array subtracting this mid from every single value in that array
02:17:26
scaling that entire array by factor and then adding back a constant mid so it's literally taking every single item in that array and adding the midpoint back in all right so now let's try adjusting the contrast of this like image so let's do increase contrast equals adjust contrast lake 2 because remember the the higher the scaling is the more the contrast we have right and let's do the midpoint 0.5 because we're working
02:18:01
on a scale of zero to one for these images and now we're going to write the image let's call it increasedcontrast.png and i'm going to do the same thing but let's decrease the contrast now so i'm just going to do the same thing except instead of 2 i'm going to pass in scaling factor 0.5 and here let's just call this decrease contrast and we're going to write this to decreasedcontrast.png let's run this okay so let's compare these so
02:18:40
this is our original and then this is a decrease contrast so you can see that it's significantly grayer and this gray just means that the contrast has decreased and everything's closer to being the same color which just happens to be gray and now this is the increased contrast you can tell that like i mean the contrast has really been increased the colors are a lot more drastic in this one right okay so the next thing that we're going to implement is a blur for the image so in the blur we
02:19:12
pass in a kernel size and this kernel size just means how wide do you want this blur to be because essentially what we're doing when we're blurring is we're taking that pixel and averaging it with its surrounding pixels and so if the kernel size is for example three then that just means we're taking a pixel and we're applying this kernel around it so it should be taking the left and the right neighbors and top and bottom the four diagonal corners for example a
02:19:43
kernel size of 15 would take the 7 to the left the seven to the right and the seven to the top and bottom and everything in that square all right so once again we are going to create a new image to make a copy to and we're just going to use a naive implementation of iterating through each neighbor and then taking the average at the end there is a faster way to do it but this again is more straightforward to understand it's more straightforward
02:20:14
to figure out what we're doing the faster way to do it would be to incorporate some sort of like memoization which means so for example what we would do is we would move like along the x-axis and every single time instead of re-summing every single neighbor we just get rid of one column and then we add in the next column and so on and that would decrease the number of operations that we actually need but again this is more straightforward so we're gonna use this way for now first we're gonna create a variable total equals zero and this is going to
02:20:45
keep track of what the total of all the summations of the surrounding pixels are and of course we need to know how many neighbors we actually have to go for so we're gonna find neighbor range as how many times does two go into the kernel so how many neighbors to one side do we need to look at essentially is what this represents and here we're gonna say for each x i in the range x
02:21:15
minus neighbor range to x plus neighbor range and remember that we have to add this plus one because range goes from the lowest to the highest minus one right that's just how python works it doesn't include the end of the range so you may think that this is all good to go but what if x minus neighbor range is actually less than zero then it would go out of bounds so here we're going to add a little bit of
02:21:47
bounds checking so we're going to say take the max of x minus neighbor range or zero so for example if x minus negative range is negative then we would say okay no cut it off at zero and same thing for x plus neighbor range we want this to be the minimum of the maximum value that we can take which would be x pixels minus one and the reason why we do minus one is because x pixels is the length right and we have to subtract the one because that's the highest possible value that we can actually index
02:22:16
into remember that python we do zero indexing so then again we're gonna do the same thing for the y neighbors and we're gonna keep these bounds because they are the same but instead of x pixels we do y pixels and then we do y plus the neighbor range every single time we go through a new neighbor we to add that value from the past and image to our total and then at the very end we can say our new image at that specific index is equal to the total
02:22:48
and then divided by the total size of the kernel so how many things did you just sum over we have essentially a box of size nine right nine elements in that box so we have to square our kernel size and then divide our total by that and so that just gives us the average value over that pixel and its neighbors and then we return this new image okay so i'm actually gonna run this blur on the city because the city
02:23:18
it has more lines in it it's more obvious if it's blurred so let's do a blur with size three blur three equals blur city comma three and then we're gonna write image and call it blur k3 and now i'm going to do the same thing with a kernel size of 15 just so that i can show you guys the difference between using a kernel size 3 for a blur and using a kernel size 15 for a blur so let's run that okay so our blur of three is done and
02:23:51
you can see that it looks slightly blurred when you compare it to the original so our blur we know that our blur is doing the job and it's still running for the 15. again this is not the fastest way to do it and the higher your kernel size is the slower the more this is going to make a difference it looks like r15 is now done okay so let's open that and now we see that this is like noticeably much more blurred than our original
02:24:22
and the reason is literally just because we've taken more pixels into account when we've created this average for that one new pixel spot okay so actually this blur that we've implemented above we've actually implemented applying a kernel to an image and so what that means is we're taking a matrix and we're applying it to every single pixel and summing up whatever values in that matrix times whatever value is at the corresponding pixel so in this blur above it's a kernel of
02:24:56
size and by n and each value is actually 1 over n squared all right let's see how we can create a function apply kernel so we can take in any kernel and we can apply it to our image so we're going to assume that the kernel is square first thing that we're going to do is we are going to again paste the code that we had above and here our kernel size is slightly different because we're not passing that
02:25:25
in instead we have a 2d numpy numpy 2d numpy array that represents the kernel that we'll use the kernel size is just one dimension of the kernel 2d array so we can just say kernel.shape 0 we're going to keep this iteration through the neighbor range and so here we need to actually find what value of the kernel corresponds to that pixel that we're at so x k is actually whatever x i we're at
02:25:57
which is representing you know x minus that neighbor range right so we're actually going to add that neighbor range back in and subtract x and you'll see that what this does is essentially it's centered around x y right so at x y that would be the center of the entire kernel but now we're trying to shift the zero for example up to the top left corner so that's that's what that those are the operations that we're doing here if you draw it out it makes a lot more sense all right and then for y we're doing
02:26:28
what i i plus the neighbor range and then subtracting y from it okay so then the value at the kernel would be kernel and then indexed at x k and y k and we add to our total this variable total we add the image at that index x i y i c but then we multiply it by the kernel value from our kernel
02:26:59
and so then the new image that array is xyc and that is equal to whatever the sum of all of these are and then of course we're going to return the new image so let us so i'm gonna so i'm gonna show this to you guys on an edge detection kernel it's called the sobel kernel so in the x direction it's going to be this array
02:27:32
one two one zero zero zero negative one negative two negative one and this y kernel will be the same except there will be some values that are switched so i can write this in a 3d format that's a little bit easier to see and so we're applying this over every single pixel in our image and now here's the y's all right so you see these are almost the same thing right let's apply this kernel to the city so let's call that symbol x apply kernel city
02:28:06
and then syllable external and then we're going to write this to edge x dot png we're going to do the exact same thing for the y kernel and now you'll see why i call these x and y so let's run this we go here and we look at the city on one side and now let's look at this edge x so you can see that this edge x really like i mean look at that horizontal
02:28:40
line right there it's an x edge detection filter and now let's take a look at edge y so you can see how this one really highlights the y edges right the edges in the y direction it would be really cool if we could just put these together and create an edge detection filter and that's exactly what we're going to do next we're going to combine these make an edge detection filter for our image so here we're going to combine images image 1 and image 2.
02:29:13
so one thing here is the size of image 1 and the size of image 2 have to be the exact same thing the arrays have to be the exact same dimensions and we're gonna again copy this shape and create a new image and we don't need any of that kernel stuff but basically what we're gonna do is we're gonna take the value from image one square it the value from image two square it add these two together and then take the square root of the sum
02:29:42
we have x y and z and index at the new image is going to be it's going to be whatever is at that index in image 1 squared plus whatever is at that index in image 2 squared and then the entire quantity square rooted so this to the power of one half is just square root and at the end we're going to return new image and up here we are getting an error because this should actually be
02:30:15
image one or image two it doesn't matter they should be the same shape right we said that like when we defined the function let's try it on sobel x and y so uncomment some of this stuff and at the very end we're going to do sobel x y equals combine images syllable x and syllable y and so then syllable x y dot write image and let's call this
02:30:48
edgexy.png alrighty so let's run this so let's set all of these next to each other so on the bottom right we have the original image then we have the x and the y filters and now check this out so this is the images combined the filters combined and you can see that this is a pretty cool edge detection filter and let me try to actually like zoom in make this a bigger image
02:31:26
so this is our image and check that out i mean you see all the edges in the image basically pretty cool look at that skyline and so yeah using all of these techniques you could literally implement photoshop in python pretty cool so the last project i have for you guys is what i call graph composer and it's kind of like an introduction to ai the idea of graph composer is derived from a markov chain and so in a markov chain you have a node that
02:32:06
represents a value and it has an arrow pointing to maybe another node that represents another value and that one might be pointing to another node which might point back to the first one and so on so you kind of create this entire network of nodes and directed edges with weights in our graph composer what we're going to do is we're going to take a text file and we're going to transform every single word in that text file to a node and then we're going to connect it to whatever words follow that specific
02:32:36
word so here's a little snippet about how these markov chain graph models actually work given a text i can generate a graph from the text where all of the words are represented by vertices and then there's a directed edge from each word to the word that follows in the text now the weight of the edge would just be the number of times that the new word follows the word that you just connected it to so let's take an example sentence how about i am subscribed to y
02:33:07
cubed and i am loving it so here we can take the letter i and we can make it a vertex and now we can connect an edge with weight 1 to am because am follows i one time so far and then subscribed we are connecting subscribe to am with the directed edge of weight one two y cubed and okay so now things get a little bit interesting because after this last and it goes back to i we already have a vertex representing the word i
02:33:39
on our graph and so and is going to connect directly to that vertex that's already in the graph okay but then we have another occurrence of i am so instead of creating another vertex for am we're gonna increase the value the weight of that edge that's currently there so instead of one we're turning that into two and then we can continue on so am is already connected to subscribed but now loving also follows am so we're going to create a new vertex
02:34:10
for the word loving and assign that a new edge with weight 1 and then of course it and now it gets connected to loving so this would be the graph that represents a sentence i am subscribed to y cubed and i am loving it so with all the songs of an artist that i've chosen i have basically created this like huge graph of all the words as vertices and edges connecting them to the words that follow
02:34:40
okay so now in order to generate my poetry i can randomly choose a starting word by randomly choosing a starting vertex and then i can traverse the graph based on the edges so these edges are kind of rules for which word you can go to next you can only follow the arrows so here's an example of what i mean by that let's take the graph that we just saw and let's say that our starting word is y so i'm at y there's only one arrow out and it's the cube so my generating thing
02:35:11
is going to be y cubed and because and is the only word that follows cubed and then of course i because i is the only thing that follows and and then am right so y cubed and i am well now we have two arrows out of am we have an arrow to subscribe and an arrow to loving we can actually choose either of these paths and in my script i've left that up to randomness so that's where these weights come into play these edge weights come into play the higher weighted an edge is the
02:35:42
more likely that path will get chosen and this is how i can generate these sentences all stitched together i'm going to show you guys how to actually implement this in python first thing that we're going to do is we're going to go to this code that i've already prepared for you what you can do here is you can download the code over here you can download as a zip file or if you know how to get clone go ahead and do that let's take a look at what's actually in these files here i have two empty files compose up high and graph.pi
02:36:13
under songs i have well i have a bunch of text files that represent song lyrics and we'll get to those a little bit later but basically we can generate lyrics using our little markov chain model i also have under texts this harry potter sorcerer's stone text file and so we'll be able to see how we can actually generate some text based off of harry potter as well and in lyrics.pie you guys don't really have to worry about this this is just a scraper that you know you input some songs and
02:36:43
then the artist name and it'll scrape lyrics genius for those lyrics basically it'll download those lyrics and save it in the files that you guys saw before so graph.pi and compose.pi are empty because well those are the things that i'm going to show you guys how to implement let's start with graph.pi also guys just letting you know that in this tutorial i will make mistakes i wanted to show you guys this because i wanted to let you know that it is very very normal to have bugs in your code
02:37:13
it's very normal to make mistakes and the important part is to actually know how to fix them so just keep that in mind while you're watching this tutorial okay so in graph.pi this is where we're actually going to have our markov chain representation and you know we know that in this markov chain we're probably going to need randomness so let's import random right now we're going to define the graph in terms of vertices naturally let's create a class called vertex first thing that we're going to do we're going to initialize it so
02:37:44
define init and we pass in a value now this value is going to represent our word from the text here we can set self.value equals value so whatever the vertex.value is that's just going to be the value that's going to be the word that it represents and here i'm going to have a dictionary called adjacent and what this adjacent dictionary is going to do it's going to keep track of you know which vertices are connected to this vertex and so those are going to be our keys and then the value of that
02:38:16
node is going to be our weight the weight of the edge from our current vertex to the adjacent one let's create a function called add edge 2 and so we have to pass in a vertex because we need to know which vertex we're drawing the edge to and let's create a weight of zero we can allow the user to manipulate the weight so let's pass in the weight but you know we can set it to zero initially just in case they don't want to pass in anything right what we're going to do is we're going to put this vertex in the adjacent dictionary and the value is again going to be the
02:38:48
weight so this is all we have to do every single time we're parsing our text whenever we see a word go to another word that's already in it's adjacent what we want to do is we want to increment that edge right so here we pass in the vertex and then we can say self adjacent vertex equals self.adjacent.get vertex comma 0. and so this dot get is just saying if this vertex is a key that's currently in self.adjacent
02:39:19
we're going to get the value of that vertex if it's if it doesn't exist then we're just gonna default make it zero and then we add one and so this add edge two is basically adding an edge to the vertex that we're inputting with some weight whereas this increment edge we're incrementing the weight of the edge from our current vertex to whatever vertex that we give it all right now that we have a bit of our vertex representation we can put this together
02:39:50
in a graph so let's create another class called graph and we're going to of course initialize this and we're just going to initialize this to an empty dictionary of vertices and the reason why we do that is so that whenever we encounter a new word we can look it up in this dictionary and then get the vertex object from this dictionary so this is going to be a string to vertex mapping all right so let's define a function get vertex values
02:40:21
and this is basically saying like what are the values of all the vertices in other words let's just return all the possible words that we have in the graph so what we can do is we can return the set of self.vertices.keys and this is just going to return all the words that we've encountered so far and then we can create a function to add a vertex into our graph so like for example whenever we encounter a new word we want to add a vertex so when we create a vertex we of course have to pass in a value that's going to be the word that
02:40:53
it represents we're going to do self dot vertices value equals vertex of that value so we create a new vertex object and we're putting it in this string to vertex mapping and of course we want to define get vertex because sometimes we'll just have a word and we want to get the vertex object that it represents so if we pass in the text the word value well okay we want to return self.vertices and then whatever is at that value right because in this
02:41:24
dictionary we're mapping it to the vertex and returning that object but what if the value isn't actually in the graph so in that case what we want to do is we want to actually add it in because if we're asking get vertex of that value well it doesn't hurt to ever add it so if the value is not in self.vertices we're going to add we're going to call self.advertx that value we're just going to create a new vertex for that value
02:41:54
and then of course return it afterwards and the most powerful part of this markov chain is that when you're at a node you can say okay get next word but based on these weight mappings here we can create a function called get next word and pass in the current vertex and what it's going to do it's going to first find whatever vertex object corresponds to that current vertex so we can say self dot vertices and then the current vertex dot value and now let's create a function called
02:42:24
next word under this vertex object and what we're going to do is we're going to randomly select a next word but based on weights all right so if we actually go to look at the random documentation we see that there's this function called random.choices where you pass in a list give it some weights and it'll actually choose randomly but based on the weights so we're going to use that idea here let's return random.choices and pass in self
02:42:59
okay but then how do we know what do we even pass in right let's introduce this concept of a probability map we're going to map each word to its probability but put them in separate lists all right in this function for each vertex comma weight in self.adjason.items remember that self.adjacent is this dictionary that has each vertex and then maps it to the corresponding weight let's create two new lists to keep track of the neighbors and then the neighbor weights and so here for every single vertex
02:43:33
comma weight what we're going to do is we're going to append the vertex to self.neighbors but we're going to append the weight to self.neighbor weights and so then we can easily pass these into random.choices so here we can do random.choices self.neighbors self.neighbor weights alrighty we actually see that random.choices returns a list so we'll have to index and get you know there's it's a list of size one but we still have to get the first item in the list so we have to
02:44:08
index at zero now we can get our next word but where are we generating these probability maps under graph we can create a function called generate probability mappings that'll get all the probability mappings of every single vertex here we're going to say for every vertex in self.vertices.values well what we're going to do is we're going to call that vertex and we're going to say hey get the probability map and so then as soon as
02:44:39
we call that function every single vertex will be initialized with the probability map and that is our graph representation so now let's move over to compose that pi let's think about what we actually need to do here well we need to get the words from the text right and we need to create a graph where the values of the vertices in that graph are those words and then for x number of words that's defined by the user we need to get the next word and then we'll just show those results to the user
02:45:09
so let's put all of these inside a main function let's start with step one getting the words from the text we can create a function called get word from text very original and pass in the path to whatever text that we're trying to get the words from we can use this command with open text path comma r r stands for read as f well we can read everything in that text if we call f.read and assign that to a variable text so
02:45:41
this is going to be a string and now what we want to do is we kind of want to split across all the white space and then join it with one single white space so what this is going to do is like if there's like multiple lines like you know indents whatever it just creates like it gets rid of all those and replaces it with a single space so text.split is going to split all of that you know white space whatever it is tabs spaces
02:46:12
um enters and replace it with a space so for example text that looks like this would become text that looks like this with only spaces all right and then what we're going to do is we're going to lowercase everything because it's easier to compare everything when it's lowercase and then now let's not try to deal with punctuation because stuff can get a little bit complex right there are cases where you might want to add a period but it's not actually the
02:46:46
end of the sentence it might be an abbreviation like for example bright side but mister is not actually the end of the sentence so what we're going to do here is we're just going to remove all the punctuation and we can do that by calling string dot make trans that's make translation string we can import string that's the python package string.punctuation is just going to be you know any of the punctuation that you can see in a string and what we're going to do is replace that
02:47:15
with empty strings so here's something like hello it's me might become hello it's me and then of course we want to split all the words and we're gonna do that by just splitting on spaces again so we can call text.split then we're just going to return the list of words so return words so under step one we can say words equals get words from text and then pass in the path to for example let's use harry potter so text slash
02:47:48
hp sorcerer's stone that's the name of the text file dot txt that's our step one right there now the next thing we want to do is make a graph using those words so let's define a function make graph and pass in the words here we have to import the graph in the vertex from this graph.pi file so up here from graph import graph comma vertex and then g let's assign this to a new graph and for each word in words we're going to check
02:48:19
that the word is in the graph and if it's not then we add it and so when we're going through this words list if we come across a new word then we want to check the previous word if it exists which it should unless you're at like the very first word in the whole paragraph so if there was a previous word then we add an edge if it didn't already exist in the graph otherwise we increment the weight of the existing edge by one and then we set our word to the previous word and we iterate
02:48:56
and so now remember that like in order to get the next word we have to set the probability mappings and so this is a great place to do it right before we return the graph object in our make graph function so let's start on this implementation for word in words we're going to check that the word is in the graph and if not then we're going to add it so let's drag graph.pi over here so we can look at both simultaneously okay so you'll see that in get vertex we
02:49:29
actually add the vertex already so all we need to do is we can say word vertex equals graph dot get vertex and then that word if there was a previous word then we add an edge if it doesn't exist in the graph otherwise we increment the weight by one so here let's actually create a previous word variable and assign it to none because at the very beginning there is a previous word here we're going to check if previous word then previous word dot increment edge word vertex right because what this
02:50:01
is going to do it's going to increment that edge between the previous word and the word vertex by one and here the increment edge already does that for you because we've implemented that in our vertex already now here all we have to do is set previous word equal to whatever this word is so that in our next iteration we have access to the previous word and we keep doing that for all words and at the very end we can say g dot generate probability mappings generate all the probability mappings
02:50:33
and then return that graph all right so step two g equals make graph and then we pass in the words that we got from get words from text now step three we want to get the next word for x number of words as defined by the user let's create a function compose given a graph and given the words and some length let's just say 50 for now we're going to create a composition and this is at first
02:51:04
going to be an empty list every single time that we generate a new word we're just going to put it into this list composition and what we can say is that well look we have the words list let's just get some random word from this words list and you know grab that vertex from g and this is where we're starting so for however many iterations in the length that the user has defined we're just going to iterate and we're going to keep getting the next word right here we can say composition.append
02:51:35
and then this word which is the word vertex we're going to append the value of that vertex because remember we can't actually append the vertex and have that legible we have to append what word that vertex corresponds to and then now our next word is going to be g dot get next word given the current word so here we can say word equals g dot get next word and this current word and what we're going to do is replace this word variable and keep getting the next one at the very end we're just going to
02:52:06
return our composition all right now down here in our main function again we can say our composition equals compose we can pass in g we can pass in words and then we can pass in some parameter length override that we can say 100 for the heck of it we don't want to just return a list let's actually return a string so we can join this list of words by a space and so this is going to return a string where all the words and composition are just separated by a space
02:52:39
so now let's run this function and generate okay and because we're returning this composition we're not printing it let's print whatever main returns and that's how we're going to see our composition alrighty so first we can try running and look at that random is not defined i forgot to define that so i go back up here and i'm going to import random save that and run it again all right so now none type has no
02:53:17
attribute value so this means that this word dot value word is somehow none so how in the world do we fix that so let's go back into our graph code and take a look at what's going on it could be g dot vertex or g dot get next word one of those is probably returning none so let's open graph.pi and if we look at get next word okay here we're calling next word but oh look
02:53:53
we're never actually returning a value and so that's our bug let's add a return statement there and let's try writing this again and there we go it worked let's read this this is built off of our harry potter text do you are some interesting so unfair that strangers they were going to let her to squeeze him i want to look at it had one who has always marooned tailcoat's orange eyes
02:54:26
away just hope of his last look back tur anyone else is very well no answer professor carroll had been a bundle of white a clear field harry ron all right so this is kind of cool but clearly this you know it's some gibberish and the reason for that is just because our implementation of simply a markov chain is not very intelligent right we're kind of just randomly choosing given this word pick a next word one way that we could
02:55:03
make it better and more slightly like english is instead of saying like hey this word maybe choose like the previous like three words right and then pick the next word based off of the previous three words and so on just so that it has some sort of memory so it doesn't jump around as much but now maybe you're interested in can we do this with the songs that i have in here and i'm going to show you exactly how to implement that we can edit a couple of things but keep
02:55:33
most of the logic the same if we go into like one of billy eilish's song for example you'll see that we have this like chorus verse thing in brackets and that's obviously not like part of the song so we're gonna remove like the bracket and the text inside of it we can do that with a regex again so what we're gonna do is we're gonna say hey substitute and then this funky expression which i'm gonna explain
02:56:03
just a bit but substitute that with the space anywhere in the text it's saying this is the left bracket this is right bracket and here this dot plus so the dot means any character and plus means one or more so this means if there's one or more characters inside the brackets then replace any version of that with a space in the text and so here in order to use this we have to import re and let's reformat some of this stuff okay and so the rest of this
02:56:34
should be the same because we still want to get rid of white space we still want to get rid of punctuation but instead of just you know one file name we actually have multiple right in this folder and one way that we can like just have python read that folder rather than having to type out every single file name is if we import os and we go down here we can actually walk through this folder and find all of the file names within that folder so let's do that
02:57:05
right here okay so for song file in here we're going to use os dot list dir so that's list directory songs and then we're gonna pass in an artist name okay so here we're gonna use the f-string pass in the artist and then in main we're gonna pass in the artist so what this is gonna do is if you pass in this artist's name to align with that
02:57:37
file name for example billy underscore irish then we're going to list every single file that's under that folder and here you know for green day you have all these for lincoln park you have all these and so on all right so after i get all these song files i can now call get words from text and pass in this path songs artist dash and then whatever the song file is
02:58:07
so here i'm gonna put that in here song file and so this is just gonna get every single song file and like go through and get the words from that and up here i'm going to define words equals an empty list and then every single time we get a song's words we're just going to add that to the words list so we can extend by whatever song words is and so now down here we want to input the artist so let's
02:58:40
input taylor swift let's try running this okay so we're getting an error and usually i would just google this error honestly this isn't one that i'm super familiar with let's actually print what the song files are so we can figure out which one it failed at and so here if we run this we actually see that it failed at this file called ds store and so this is just some cache that's stored under this directory it's not actually a song file itself and so we can just say like if this equals
02:59:12
dot ds door then continue because we're just going to keep going the loop and so if we run compose.pi again look there's our taylor swift masterpiece i remember thinking are we in red because there you breathless hmm or it's time you need to calm down you're sorry for it used to hit you again even if it's such a chance to paper airplanes flying flying and i'd never looked back together
02:59:43
etc again this is kind of gibberish we could make it more intelligent but for now as a beginner project this is pretty cool you're generating paragraphs based off of some vocabulary that you're inputting through songs or harry potter etcetera if you enjoyed this video be sure to subscribe to my channel follow me on instagram and twitter at kylie y ying if you guys are interested in live coding sessions
03:00:14
where you know they're not pre-planned like these tutorials you should definitely follow me on twitch kylie ying alright hope to see you guys around i hope you guys enjoyed my videos and yeah see you later

DOWNLOAD SUBTITLES: