All right, so I really hope you've given this challenge a good go and you've spent at least an hour
toiling over it and struggling, and you should be wiping sweat off your brows before you come and look
at the solution.
The struggle is so important and I can't stress it enough.
But in this lesson, I want to run through how I would tackle this problem.
Now, it doesn't mean that you have to do the same thing.
There isn't one solution.
There's probably thousands of different solutions for a project like this.
And as long as your program runs exactly the same way, then you've succeeded.
It doesn't matter at this stage how you've done it necessarily.
As long as you manage to organize your thoughts, break down the problem and write the solution code
and get it to run, then you are victorious.
The real challenge in today's project was for you to break down a big problem into smaller, solvable
chunks.
It isn't even so much the code that matters.
It's this skill that you really need to develop to become a really good programmer.
And so far I've been breaking down the problem for you, showing you the steps that would take you to
the solution.
But now you're really wearing the big-programmer pants and you're doing it all by yourself.
So let's think about this problem and how we would break it down.
There's some easy problems here, right?
We need the art to display.
So let's add a comment for each problem we identify.
We've got the ASCII art showing up here and we've got the versus showing up here.
Now, the next one is we've got these randomly generated accounts right from the data.
So we've got Camila Cabello and Emma Watson.
How do we get these random accounts to show up?
That's another problem, right?
So we know that we've got all of this game data here in the format of a dictionary that's nested inside
a list.
And we need to figure out a way of pulling out a random account from this list.
So that's another problem I've identified.
Now, the next thing you'll notice is that the data contains many key value pairs, the name of the
account, the description, etc. But when we display it, it's all nicely formatted; the name and then
what they do and then where they're from. We'll have to add that as probably another problem to solve.
How do we format the account data into a printable format?
OK, so let's say we solve that and we've got A and B, now we have to ask the user for a guess.
Guess who has more followers.
So that's another problem to solve.
And when they do type an answer, let's say we think maybe A has more followers, then it's going to
have to check that answer and see if it's correct or if it's wrong.
So that's going to be a big problem.
Check if user is correct, because that's going to require a couple of things,
right?
We're going to probably have to break down this problem into smaller problems if we want to solve it.
So, for example, we probably need to figure out the follower count of each account, and then we'll
probably need to use an if statement to check if user is correct.
Now, if we get it right or if we get it wrong, the game is always going to give us some feedback.
"Sorry, that's wrong" or "You're right."
So let's add to our problem list. Now,
in addition to giving them feedback whether if they got it right or whether if they got it wrong, it
also has a way of tracking score,
right?
Every time they get the answer right, the score goes up by one.
In addition to all of that, the game repeats itself when the user gets it right.
So let's say in this case, I guess Dwayne Johnson, then you see how the game repeats itself, right?
It goes back and it takes Dwayne Johnson puts it in position A and generates a new account in position
B for me to go again.
So I get another round because I got it right.
So we'll need to address that in our code as well.
Have some sort of way of make the game repeatable.
And it's not just repeatable, actually, because we also have to move the previous B up to the A position
if they do get it right for the next round.
So I think Khloe Kardashian maybe has less.
Oh. So let's say, I guess Dwayne Johnson again in this case.
Now, if I get it right, Taylor Swift, who used to be at position B now gets moved to position A,
ready for the next round.
So this is another problem we have to tackle.
So not only does the game have to be repeatable, we also have to have some sort of way of making the
accounts at Position B become the next account at position A.
And finally, notice that in between the rounds, if I get it right or if I get it wrong, the whole
game actually clears the previous progress other than letting me know that I got it right and my current
score
and it displays everything afresh.
It doesn't clutter up the screen and I have to scroll down.
So that's probably the last problem we'll need to tackle.
So clear the screen.
That's it, I think for now. I've broken down this large problem, which is build this game, into a bunch
of smaller problems and as we code along, we're probably going to break them down even further.
This really was the key skill that I wanted to test you on, being able to take a big project, turn
it into smaller problems, and then tackle them one by one.
So if you manage to get this far and you've managed to identify all the problems that you need to solve,
you're ready should consider yourself successful.
Now, the next stage, we're going to try and tackle each of these one by one and be able to create
the final version of the game.
Let's get started. Now, the first thing is probably the easiest, so I'm going to start from there and
I'm actually going to go in the order that I've written the problems rather than picking them off from
the easiest.
I think this just makes it easier for you to understand what's going on when I'm writing the code.
But in your case, you can pick off the problems in whichever order you want as long as the final version
works as you expect it to. To display the art which is going to be the logo to begin with,
I'm going to have to import that art file. And I'm going to use the from art import logo.
This way, I don't have to write art.logo every time I want to use it. Now I can simply just write print
logo.
So that's the first one tackled.
That was pretty easy.
Some of them are going to be a lot harder than that.
The next one is to generate a random account from the game data.
And remember, the game data is here in a separate file and the data is held inside a variable called
data.
So let's go to the top and let's do the same thing. So from game_data import data. And now we can
use that data to generate a random account.
In order to generate a random item from that list, we have to use the random module,
so I'm going to import the random module. And I can use that random module to tap into the choice function
and pass in my data, which is a list.
So this function is going to take this list and then find a random entry in there which will give
me a random account.
Let's call this account maybe account A and set that equal to the output from this random choice function.
Now, I'm probably going to need more than one random account because I need to compare A against B.
By calling random choice on data
again, I make sure that I get a different random account.
So at this point, we've got account A and account B, which hopefully are different from each other.
But if you wanted to, you could in fact check and just make sure.
So if account A equals account B, then in this case you make sure that account B is regenerated.
Often, as you're coding along, tackling the problems, smaller problems arise and you start thinking
about what could happen and you start addressing that with code as well.
So I think we're now done with this second problem and we're ready to move on to the next one.
Let's format the account data so that we can print it out in this format.
So the account name and then A and then whatever it is that they do,
and finally from and where they're from.
So we know that the data of each of these accounts is basically a dictionary.
And we know that we access dictionaries using the key-value pair. So we could, for example, access
the account name
by tapping into the random account, let's say, account A, and then we use that square brackets and
then we put in the key in here, which let me just check, is the name.
So let's put that right here.
And this will go into that dictionary and pull out the value under the key name and save it to this
variable.
And we can do the same thing for the other two pieces of data we need.
And just make sure that you don't have any typos in your keys here, because remember, they need to
match these precisely in order for it to not error out.
Now that we've got all of these pieces of data, we can now print the final version. So we're probably
going to use an fstring to insert all of these pieces of information, and we'll probably start off
with the account_name and then a comma. We can write a and then put in the account_desc, and finally
put from and the account_country.
That's pretty much it.
But notice how I've done this just for account A and I actually have to do it for both Account A and
account B. So instead of repeating all of this code, it might be a lot easier if I actually pulled
this out into a separate function.
So let's create that here.
Let's create a function that does this.
I'm going to turn this into a docstring to explain the function, and I'm going to call the function
format_data and it's going to take an input which is the account.
And now using this account, we can make this more reusable by deleting the _a part of it.
And of course, we need to indent everything over so that it's actually within this function. And instead
of printing the final outcome, I'm going to return it as the output.
So now we can call that function down here and we can pass in each account in turn. For example, we
could say print "Compare A: " which is going to be first_account.
So we're going to use our fstring to insert the data that we get back from our function call.
And I'm going to call it within the print statement.
You can, of course, take it out and create a separate variable,
but I think this should be clear enough to see what's going on.
So we're going to call format_data and we're going to pass in the account A for this first line, and
then I'm going to do the same for B.
So in this case, I'm going to pass an account B and it's going to format the data and then print it
out.
So as I always say, it's a good idea to run your code frequently.
Probably any time you solve a problem, you should probably run it and see if it works the way you expect
it to.
So the first one, the logo gets printed. The second one, we've managed to get some random accounts
because every time I run it, the accounts should be different.
And finally, we format the account into this format with their name, their description and their country
all written out from the print statement using this lovely format_data function we've created.
We've already done really well
so let's keep going. Now,
at the moment it just says compare A, compare B. But instead, it'd be much nicer if we can put in that
art, the versus ASCII art in between the A and B.
Now you could write the line again from art import versus, but instead you could also just add a comma
here and write versus.
So now it's going to import both the logo and that versus art and we're going to put it right here.
So we're going to use a print statement.
And instead of using a string inside double quotes, we're just going to write the name of the variable
which is called versus.
And if we take a look at the final version, you can see it also says compare A against B, so we'll
change the second word to against.
And now when we run the code, it should look pretty much identical to the starting point of our final
game.
Let's tackle the next problem. We need to ask the user for a guess to see who has more followers, right?
So I'm actually just going to copy this string right here and I'm going to put that into an input.
So who has more followers, A or B, and this input is going to come back as a string and I'm going
to save it inside a new variable called guess. Now because it asks them for A or B, they might type
A like this 'a' or they might type A like this 'A.'
Ideally, we should make it so that it doesn't matter what they typed.
It'll still be valid as long as they typed an A. So we can turn this all into a lowercase format.
So if they typed it capital like this, it'll become lower. If they typed it lowercase then it's already
lower and it will be fine as well.
So that way it's more consistent.
So now our final product actually looks pretty much exactly identical, but it doesn't really have much
gaming functionality yet.
To do that, we have to move on to the next problem and check if the user got it right. In order to check
if they got it right,
the first thing we'll need is to know how many followers they each have,
right?
Each of the accounts, A and B. Let's get a a_follower_count
and that is going to be using the account_a and then pulling out the data under the key follower_
count. So I'm actually just going to copy it to make sure I definitely don't make a typo and I'm going
to tap into account_a and then pass in that key follower_count remember as a string because our
key is a string.
So when we use it also has to be a string inside double quotes.
And I'm going to do the same for the b_follower_count from account_b.
Now I've tackled that mini-step inside the problem.
The next step is use an if statement to check if the user is correct.
Now, again, I think this piece of functionality would be really nice if it was reusable.
So I'm actually going to pull out of this main sort of flow of the game and define a separate function
at the top.
And this function is going to be called check_answer and it's going to get the guess,
the a_followers
and the b_followers.
And I'm going to add some docstrings.
So the docstrings are really important, especially when your function returns something. For example,
in this case, it should actually say takes the account data and returns the printable format.
That makes it so much clearer what this function actually does,
what are its inputs and what are its outputs. In our check answer
then it's going to take the user's guess and the follower counts of A and B and then returns if they
got it right.
So how might we write this?
Because the logic is actually a little bit difficult.
We need to check which account has more followers and then we need to check if the user actually got
it right.
So it's almost a two step process.
And I think in these cases it's actually really worth sort of sketching it out.
You can use a pen and a piece of paper.
I'm just using Skitch right here so I can show it to you more easily.
So let's just construct a simple sort of two by two matrix.
And at the top, we've got the-- let's say the user guess is A and an alternative is they guess B.
Now, these are the user's guesses, but then it's going to be compared against the actual reality or
the answer, which is that A has more followers than B, and the alternative is that B has more followers
than A. If the user guessed A and A indeed has more followers than B, then they got it right.
And similarly, if they guess B and B did indeed have more followers than A, then they would have
also gotten it right.
But if it was the opposite case, as in they guessed A but actually B had more followers, then they would
be wrong.
And also if they guessed B and A has more followers, then they would be wrong as well.
Now, using this table, we can now construct our if statement.
There is a very, very long version of it, and there is a much shorter version of it. So the very
long version would check for something like if the a_followers is greater than the b_followers,
so A has more followers than B, and the guess is equal to A.
Now you can go through this kind of way and create four of these if and elif, else statements.
But it's very easy to get lost in some of the logic.
And also it means you have to type a lot of this out instead of using the else as the alternative.
So I think a much easier way would be to first check if A has more followers than B, and in this case
you can then check
well, if the guess is equal to A, well then that means they got it right
so I should return
true. But if this was not right, then I should return false.
An alternative and a much easier way of expressing this is to simply return
guess == a because when this is evaluated, is guess equal to a, then it's going to return
true. But if guess is not A, if it's B, then it's going to return false.
So that's a little bit mind bending,
but if you actually go through it enough times, especially using a debugger like Python Tutor to
just see how it works, then it should become pretty clear pretty quickly.
In my case, all I need to do to express my table of possibilities is to first check if A is greater
than B. Now else, namely, if the opposite was true, B was greater than A, then I'm going to return
if guess is equal to B.
So now basically the code says if A has more followers and they guessed A then return
true. If B has more followers and they guessed B then return true.
And if the opposite happens, then return false.
Basically, it's going to return whether if the user got the answer right.
Now, we can basically check that in here and we could create a new variable could is_correct
and set that to equal the output of our check_answer function.
So we're going to call it and pass in all the necessary things, the guess, the followers from A and
B. So guess and then the a_follower count and the b_follower count.
Now, once we know whether if they got it right or not, we can tackle the next step, which is give
user some feedback on their guesses. So we can check,
well, if is_correct--
well, in this case, we should probably print something like "You're right."
But if they didn't get it right, then we're going to print sorry, that's wrong.
Now, in addition to giving them feedback, we should probably also keep track of the score. So we can
start off our game with a score of zero.
But as they play on and if they get it correct, then the score should be incremented by one each time.
And in addition to printing out, "You're right,"
we should also give them some feedback on the current score.
And if sorry they got it wrong, then we should also give them the final score,
because at this point the game is going to end and they should know how well they did.
So now let's go ahead and play our game and see if it works.
So compare Cardi B against Kourtney Kardashian.
I think it's probably going to be B, and it tells me you're right and current score happens to be inside
curly braces.
So there's going to be a lot of debugging that you're going to need to do while you're writing this
code,
and you're probably going to make a lot of mistakes.
But everybody does them, including me.
If we get it right, we get the feedback, you're right and the current score. If we get it wrong,
then we get the answer
Sorry, that's wrong.
And the final score as well.
That's all working so far.
Now we have to figure out some sort of way of making the game repeatable. And, as you might remember,
that's usually going to require some sort of while loop.
But it's important to think about which parts of our game actually repeat themselves,
right? Because when we play the game, it looks like everything's kind of static.
But if we think about it, the score is set to zero probably can't be repeated, but the accounts
probably do need to be generated and they do need to be randomized again.
And then we need to ask the user again, compare A against B and then ask them for an input and then check
the answer.
So all the rest of this probably needs to be repeated.
So I would say everything from here probably should be within our while loop.
So what is our while loop going to check?
Well, maybe we could create a flag like game_should_continue and start out as true.
And then while that's still true, then we'll repeat all of this code below.
So let's indent that into the while loop. Now, at which point should this become false?
Well, they get to keep going as long as they get the answer right,
but the game ends as soon as they get it wrong.
This is the point where we're going to change
game_should_continue to false so that once it prints "Sorry,
that's wrong."
It's going to end the game.
Now let's try and run the game and see if we can get our loop to repeat itself.
I'm going to choose A, and you can see you're right, current score is one. And now it's telling me to compare
A which is a new random person against B, a new random person.
As long as I get it right, it will allow me to keep playing the game. But as soon as I get it wrong,
so I'm pretty sure that National Geographic sadly doesn't have as many followers as Kim Kardashian.
So I think this is going to get me to exit the game.
Sorry, that's wrong.
Your final score is three, and as you can see, my game has now ended.
But notice how in the final version of the game, the count at position B actually becomes the one at
position A in the next round.
So the game kind of continues like this.
And then once I guess this, Kim Kardashian is now going to become the one at position A and it keeps
on going as long as I got it right.
So we now need to code this functionality into our program.
At the very beginning,
we generate a random account A and a random account B. But instead, what we want to happen is we want
to make sure that the account at position B becomes the next account at position A.
So once this game has run its course and we're looping back to the beginning again, the account A
should become the previous version of account B. But this obviously will give us an error because there
is no previous account B. So what we could do is we could actually take this out of the loop and say
account B is equal to a random choice of data and account A becomes that account B. But then the actual
account B is going to be a new random account.
Let's play this through in our heads as a computer.
The first thing that happens when the game starts for the very first time is we generate a random account
B. Now that account becomes the account A and then account B becomes a new random account.
Now when the game loops back, it's not going to touch these parts of the code.
It's going to start from here.
So now the account A is going to be replaced by the previous account B, and this new account B is going
to be a new random account.
But what if the new random account is still the same account?
How can we get the code to continue checking until it has found a different account?
Well, we could replace the if statement with a while loop, then the code will keep checking until
they are no longer equal.
And this way, when we run our code, you can see that as long as I get the answer correct,
then the previous B becomes the new A and B now becomes a new random account.
That's pretty much all of the functionality sorted, right? But we've got a couple of things to tidy
up.
One is we need to clear the screen in between the rounds of our game so we know that we can import the
clear function from the replit module.
But here's a question, where do you call it? Because if you call it at the very beginning of the loop,
like here, then what's going to happen is that your logo is never going to be displayed.
And also, when you actually get the answer correct, like we will here,
the result isn't shown because the result is shown at the very end of this while loop. And once that's
done, it's going to loop right the way back here and it's going to clear everything in the console.
And that's all going to happen in a fraction of a second.
So the user won't actually see it.
So we need to move this clear function.
The ideal time, I think, to actually show it is just before they're about to see the outcome of their
action.
So somewhere here.
So now, once we've asked the user what we need to ask and we've got their answer, we clear the screen
and then we show them the outcome.
Now we get to see the logo,
we get to see all the versus and A and B, let's select B and we got it right.
So we get to see that feedback as well.
It clears the screen and we have a new comparison to make.
Now, the only thing you'll notice is that the logo disappears in each successive round.
It might be nice to see it even if we're going to the next round.
So to do that, we have to print the logo just after we clear the screen right here.
So now, even though we've cleared the screen, we still get to see the logo again as long as we get
the answer correct.
That's all there is to it.
We've now completed all of the functionality that's in the final version of the game, and it took quite
a bit of code at the end.
But as long as you broke it down into these smaller components, each of the components and each of
the problems are much, much easier to tackle.
I hope you enjoyed building this game and you have fun playing with it.
I hope you enjoyed building this game.
And I'll see you on the next lesson.