COLTON OGDEN: All right.
Good morning, good
afternoon, good evening,
depending on where you are in the world.
This is CS50 on Twitch.
My name is Colton Ogden,
and I'm joined today by?
RODRIGO DABOIN SANCHEZ:
Rodrigo Daboin Sanchez.
COLTON OGDEN: And Rodrigo has
joined us before on stream--
well, he joined us
yesterday-- yeah, while we
were doing a little bit of Minesweeper.
And it's sort of tangentially
related to today in that today we're
covering a game, right?
So what are we talking about today?
RODRIGO DABOIN SANCHEZ:
Today's Game of Fifteen.
So if you were here
yesterday, you would have
remembered us showing the little visual
of what it looks like when it's solved.
But yeah, we'll talk about it and--
throughout the whole stream.
And it's essentially just what
is a board game in a fiscal form.
But we're doing it terminal based so
just printing out onto the terminal.
And you're just trying to
unshuffle a board of numbers
so that it goes from 1 to 15
with an empty slot at the end.
And we'll see what that looks like.
COLTON OGDEN: The first time
I think you were on stream,
was it for Super Mario Brothers?
RODRIGO DABOIN SANCHEZ: Yeah,
I think it was for Mario.
So I've been on stream
twice just playing games--
figured I had to come on it, actually.
COLTON OGDEN: There was Zelda, right?
You also did Zelda with us?
RODRIGO DABOIN SANCHEZ:
I did Zelda as well.
COLTON OGDEN: Yeah.
We did a couple of streams last
year, towards the holiday season.
We ended up doing some
actual, proper Twitch content.
Normally, people play games on Twitch.
We program games on Twitch often.
Why don't you tell people about
[INAUDIBLE] your experience at Harvard
so far--
what you're studying, how
long you've been here.
RODRIGO DABOIN SANCHEZ: Sure, yeah.
I'm a junior right now, about
to start spring semester.
And studying computer science--
kind of a newbie in the field
because for the longest time,
I thought I was going to do math.
And then when I was
in my freshman year--
actually, the-- yeah--
no, the beginning of
my sophomore year, I
realized I didn't really like
math as much as I thought I did.
And CS, instead, was
kind of related to math
while still more entertaining for me.
So I switched to that.
And I'd had just a couple of
years of experience with CS
before that, mostly
with Java, Mathematica.
And so I just took CS50 as a freshman.
And here I am, two years
later, working for CS50, so--
COLTON OGDEN: Awesome.
Well, it's good to have you.
You also worked with me
for the games course.
RODRIGO DABOIN SANCHEZ: That's true.
COLTON OGDEN: You and I worked together.
We taught the games course last--
you were in during the
summer version, correct?
RODRIGO DABOIN SANCHEZ: Yeah.
COLTON OGDEN: Yeah,
not the spring version.
Yeah.
And that was awesome.
Yeah.
That was really cool.
Some folks in the chat I think are
actually taking the games course
online right now.
One of the person's name,
[INAUDIBLE],, and I'm not sure
if they're in the chat at the moment.
Let's look at the chat,
actually-- just make
sure we're caught up with everybody.
So we have Harvey Neto from Brazil.
So hello, Harvey.
Good to see you.
RODRIGO DABOIN SANCHEZ: Bon dia.
COLTON OGDEN: I'm not sure if
Harvey's been in this chat before.
So welcome, if this is your first time.
Oh, indeed.
Yeah.
Now they're a first time follower
as well as of a few minutes ago.
So Harvey, thank you for the follow.
And also TaserHD, thank
you for the follow.
[INAUDIBLE],, is on, Bella_Kirs,
[INAUDIBLE]---- hello from Germany.
Hello, [INAUDIBLE].
Good to have you with us.
That's a new name as well.
Edwine21 says hello.
That's a new name.
Whipstreak23, Nate, hello.
Thank you for joining us today.
[INAUDIBLE], hello from Egypt.
A lot of new names today.
A lot of representation around
the world, which is great.
Love it.
Andre's very kindly
giving everybody hello.
Bhavik's not here.
Oh, that's a good point.
Bhavik's not here.
That's unfortunate.
Hope he joins.
I did realize that we forgot
to post a pub yesterday.
We pubbed today.
We didn't pub this one yesterday.
So hopefully that's not the reason why.
If so, that's on me.
So I apologize for not doing that.
But we've mentioned it on
stream before, certainly.
And then again-- and
in case I do forget,
which I won't, about tomorrow's.
Tomorrow, David's doing
a stream on Render 50.
So tune in for that one for sure.
And glad that all of you are here
today that have tuned in right now.
Let's see who else we have.
We have Forsunlight.
Hello, Forsunlight.
Bella_Kirs, Twin Tower Power.
Are we going to get to see Rodrigo
bless us with his knowledge today?
Why don't you answer that question.
RODRIGO DABOIN SANCHEZ: You know, it's--
knowledge is a funny thing.
No.
Yeah, yeah.
We'll get to see a little
game that-- it's actually
inspired from a previous CS50P set.
But it was coded in C. And so I
decided to do it from scratch in Python
and implement a solving
algorithm for it.
So it's inspiration from the old, but
some of it is from scratch from me.
So we'll see.
COLTON OGDEN: Yeah, we didn't do--
we've done Game of Fifteen historically.
But within the last couple of years--
I'm not sure if it's the last two years
that we haven't, or just the last one
year.
I think it might be the last two years.
We've switched it out for music and
something else this last semester.
RODRIGO DABOIN SANCHEZ: When
I took it as a freshman,
that was the last year
that we had it as a pset.
COLTON OGDEN: OK.
Was that two years ago?
Three years ago?
RODRIGO DABOIN SANCHEZ: Yeah, 2016.
COLTON OGDEN: OK.
So the last two years, we
haven't actually done it.
But it's nice to bring it back.
There's a lot of-- you can go into
old versions of the course on YouTube
and whatnot and see that that
course-- or that problem set existed.
Will the stream with David
tomorrow be at the same time?
I can tell you right now.
No, tomorrow the stream is
going to be at 3:00 with David.
So tune in for that one at 3:00 PM.
Harvey Neto, hello.
Who is from Brazil.
Forsunlight, I'll try to listen only.
Everybody say computer science.
Martian Mars Boy says yes.
And that's a new name that I haven't--
I don't think I recognized.
We should make a group on
WhatsApp, says Harvey Neto.
Everybody's super enthusiastic.
Tuxman29, thanks for joining.
And-- oh, there's Bhavik at the bottom.
He's here.
He was just a couple of minutes late.
Well, I shouldn't say
a couple minutes late--
a couple minutes later
than everybody else.
We haven't actually started
the implementation yet,
but glad to see Bhavik
there as a regular.
And Tuxman says studying CS
in Montreal because of CS50X.
That's awesome.
Cool.
I'm going to bring us to the shot
of your computer, which is just,
at this moment, an empty terminal.
And I'm going to let you
take control at this point
and guide us through what
you want to illustrate.
So go for it.
RODRIGO DABOIN SANCHEZ: All right.
Well, let's just-- let's-- for
those who are not familiar--
actually, I can do this
from the terminal now.
So if I write a bunch of just
random things in the terminal
that you haven't seen before--
I like aliases a lot,
and so actually I can--
COLTON OGDEN: So GC for Google Chrome?
RODRIGO DABOIN SANCHEZ: Yeah.
I might have some jobs right now,
so let me just exit out of that.
But if I actually open up Atom here,
I have some nice little aliases.
So I'm really lazy, and I don't
like typing things out very long.
So for committing to git, I
have gcm instead of having
to write git commit, blah, blah, blah.
COLTON OGDEN: Oh, I like that.
RODRIGO DABOIN SANCHEZ:
So just random things.
I don't have to write the same
thing over and over again, so--
COLTON OGDEN: I should
probably do that too.
RODRIGO DABOIN SANCHEZ: If I navigate
pretty quickly in the terminal,
it's just because I kind of saved
future me the headache of writing
all this stuff unnecessarily.
But OK.
So here I am.
What I was going to do
is show everyone who
hasn't seen Game of
Fifteen what it looks like.
COLTON OGDEN: Oh, good idea.
That's a good image on the right.
I like that.
The 15 puzzle.
And some people might
know it as 15 Puzzle.
That's-- maybe it's a more--
RODRIGO DABOIN SANCHEZ:
Might be a little bit small.
COLTON OGDEN: --popular name.
RODRIGO DABOIN SANCHEZ: Yeah, 15 Puzzle.
So this is what it looks
like when it's solved.
It's, like I was alluding
to before, a two by two--
like a 2D array type of thing,
where you have 16 empty tiles.
And then 15 of them are filled up
with tiles numbered 1 through 15.
And so what you're trying to do in
the physical board game is just--
they're not-- they don't
start off solved like this.
They'll start off in some scrambled way.
And you're trying to move the
tiles down, and to the left,
up, and right so that you end
up with this configuration.
And we're going to do
this on the terminal.
So that's about it.
COLTON OGDEN: Tuxman says I
was teaching coding to kids,
and a phrase that they always use,
coding is the art of being lazy.
RODRIGO DABOIN SANCHEZ: Exactly.
I always tell everyone,
being lazy is the best
feature a programmer can have as long
as they still get their work done.
COLTON OGDEN: Yeah, I
kind of agree with that.
Rabin Gaire also says hello there.
So, hello Rabin--
Rabine.
Good to have you with us.
What were you going to say?
RODRIGO DABOIN SANCHEZ:
Yeah, because being lazy
makes you look for the most
efficient solution, the one that
takes the least amount of work.
COLTON OGDEN: There's that
quote from Bill Gates.
It's like, he'll always choose
the lazy programmer-- or whatever,
the lazy worker, because they'll get the
job done with the least amount of work.
RODRIGO DABOIN SANCHEZ: Right
COLTON OGDEN: I don't know if
that's actually a real quote.
I feel like I hear it
all over the place.
RODRIGO DABOIN SANCHEZ: So what I'm
doing right now is I am just creating--
COLTON OGDEN: So touch,
to be clear, is a--
RODRIGO DABOIN SANCHEZ: Right.
So right now, I've just created two
new files using the touch command.
And it just creates
an empty file for you.
You give it the name.
And so here it is now in my
text editor, which is Atom--
which is a different
one than Colton uses.
COLTON OGDEN: Right.
RODRIGO DABOIN SANCHEZ: I
don't remember which one--
COLTON OGDEN: Yeah.
I use VS Code.
But they're very similar.
They're sort of electron based, modern
text editors with a very well supported
plug-in system--
plug-in ecosystem.
VS Code has some built-in debugging
stuff, which is pretty cool.
RODRIGO DABOIN SANCHEZ: That's nice.
COLTON OGDEN: V1Rani's in
the chat saying, hey, guys.
Been here yesterday, decided
to stop by today as well.
So thanks for popping in again.
Glad to have you with us.
RODRIGO DABOIN SANCHEZ: For sure.
And I know we don't do a lot of
Python coding on the stream, right?
COLTON OGDEN: Not yet at least.
RODRIGO DABOIN SANCHEZ: Not yet.
COLTON OGDEN: It would
be nice to do more of it.
I know that for sure.
A lot of people are into Python.
I love Python.
I don't use it in the context of game
programming, but yeah, absolutely.
I think we should definitely
have more Python stuff.
So this is a good step
in that direction.
RODRIGO DABOIN SANCHEZ:
And so with Love2D,
which is what Colton usually
uses when we're creating a game,
you have the update and the render
and the load and everything.
But with Python, you don't get
that kind of a built-in behavior.
You have to implement it yourself.
And so in Python, if
you want to do something
as simple as printing, hello, world--
or just hello, you literally just
write it in whatever file-- usually
it's called main.
And then if you run it here,
you would write Python 3.
But once again, I'm lazy.
So I just write py.
And that's how you
execute a Python program.
So all of a sudden, we have
our first Python program.
And before I jump into the actual
coding itself, what I like to do
is build myself a little roadmap.
So what I'm going to do is create
another file called plan.txt.
And I just like to write out what I need
to implement the program before I just
dive into it.
That way, it's kind of like
writing a rough draft of a paper
before you-- or an essay
before you dive into it.
COLTON OGDEN: Yeah, absolutely.
I do that too-- actually, a lot--
when I'm working on a
larger project and like some
of the ones you've done on stream--
like something that's
maybe more representative
of a real world game code base.
It's very easy to get lost in--
RODRIGO DABOIN SANCHEZ: Yes.
COLTON OGDEN: --figuring out
where you want to go next.
So having a-- some sort of to-do--
I mean, essentially, it's
the idea of just a to-do--
RODRIGO DABOIN SANCHEZ:
It's a to-do list--
COLTON OGDEN: --list, generally.
RODRIGO DABOIN SANCHEZ: --basically.
COLTON OGDEN: I mean, we
use Asana for work, which
is a great tool for to-do list stuff.
And it's that sort of idea--
people that can keep track of all
the individual things they do.
I feel like it's easier
to stay on top of stuff.
RODRIGO DABOIN SANCHEZ: So I'm
just making a checklist here,
things that we need.
And if we-- oh, I got rid
of the image, didn't I?
If we go back to--
COLTON OGDEN: Lancemaker is saying,
dude, I have a huge Excel table,
and I have to crush it down for work.
I'm using Python since I suck
at Excel, and Python also.
But still, Python is pretty comfy.
RODRIGO DABOIN SANCHEZ: So if we're kind
of taking a look-- oh, what was that?
COLTON OGDEN: They're basically just
saying they have a huge Excel table
and they have to crush it down for work.
They're going to use Python instead
of Excel because Python's pretty nice.
RODRIGO DABOIN SANCHEZ: Yeah, Python
gives you a lot of flexibility.
And we actually-- David teaches
a class at the business school
that starts off in Excel, then
talks about database languages,
and then moves on to Python.
And it's just kind of like the different
things that you use these tools for.
And obviously Python gives
you a lot of flexibility, so--
but, yeah.
So if we're looking at
this board right now,
and we're thinking about if we're
writing a video game version of this,
what do we need in order to code this?
The first thing that comes
to mind is, well, we actually
need a way to represent
the board, right?
And so I preemptively created
a file called Board.py.
But in the plan, let's
just jot to ourselves,
we're going to need a 2D array,
probably, to represent the board.
And then we're going to be,
obviously, moving it around a lot
as we try to solve the game.
And we should ideally keep
in mind an image of what
the game looks like when it's solved.
So maybe we're also going to
need a 2D array to represent
the solved board or the goal.
All right.
If we take another look at
this, we see that there's always
going to be this empty slot.
And as we shift tiles down or right, to
the left, it's kind of moving around.
So it might be useful to keep track
of the position of this empty tile.
So if we think about a 2D array, this
might be position 0, position 1, 2, 3.
And then this is row 0.
And then this is row
1, position 0, 1, 2, 3.
So we'll probably want to
keep track of this empty slot
because if we think about how we're
going to implement shifting tiles, what
we'll probably want to do
is just swap the location
of the empty tile with the tile in the
direction that you're trying to move.
So it'll be nice to have a
reference to that position.
So we'll need a location variable
to keep track of empty tile.
COLTON OGDEN: The chat's curious about
your distro of Linux, by the way.
Do you want to share?
RODRIGO DABOIN SANCHEZ: Oh, yeah.
I have-- lsb_release dash a--
Ubuntu 18.10 cosmic.
COLTON OGDEN: Nice.
RODRIGO DABOIN SANCHEZ: And I
actually have to give a shout out
to one of my co-workers, Karin Zedan.
He helped me.
I was actually using Windows before.
And I wasn't very familiar
with the Windows terminal.
And what I had to do instead
was download a batch subsystem
that ran Linux on Windows.
And it was nice for a while,
but it's still kind of growing,
and it hasn't implemented all
the features that it needs.
So some things that I wanted
to run on it, didn't work.
And eventually, I decided
to dual boot my computer.
So this computer is actually running
Windows and Linux, but separately.
And so when I shut it
off and turn it on,
I can pick which operating
system I want to use.
COLTON OGDEN: That's pretty cool.
RODRIGO DABOIN SANCHEZ:
So yeah, it's pretty nice.
I can program in my
Ubuntu, and then if I
want to do Microsoft Word or whatever,
I can switch over to Windows.
So you know, that's nice.
So where were we?
Yeah, so we're talking
about things that we need.
We'll probably want to be
able to see the board somehow.
So we want function to
display board on screen.
Let's see.
What else do we need?
We probably need-- if we want
to make moves with the keyboard,
we'll probably need
some sort of listener--
listener for keystrokes.
Which, remind me, does that
come built into a Love2D, right?
COLTON OGDEN: It does.
RODRIGO DABOIN SANCHEZ: It does?
COLTON OGDEN: Yeah.
Love2D has a function
called love.keypressed,
which is a callback function
that Love will trigger.
There's a hook in the executable
that is always pulling for input.
And whenever it gets any keystroke
or any interface, mouse, keyboard,
joystick, whatever, it goes
through all the callback functions
that you've registered
via love.keypressed.
Well, it goes through all
the callback functions
it has registered and then
calls the logic you've defined
within those callback functions.
RODRIGO DABOIN SANCHEZ: Yeah.
So that's pretty nice.
In Python, when I was tinkering
around with the idea for this game,
I actually realized that I was going
to have to download a library in Python
to help me keep track
of a keyboard listener.
So we'll have to take a look at
what library to use for that.
And the game's not too complicated.
So basically the last
things we'll likely need
is just, if we want to solve the game,
maybe a function to solve the game.
And then how do we know that we're done?
We probably want some sort of
"game over" screen or something.
And I think that's a pretty
good starting point, right?
This is a nice little to-do list.
We can knock these out one by one.
COLTON OGDEN: I usually
do my "game over"
screen, ultimately, very, very last--
once I'm on stream at least.
RODRIGO DABOIN SANCHEZ: Yeah.
I mean, it's not really
like a functional thing
that the program needs, but
it's kind of like a nice, OK--
COLTON OGDEN: It's a nice way
to package it up with a bow
and be like, oh, here's a game
that's actually complete, more
or less-- or at least feels complete.
RODRIGO DABOIN SANCHEZ: Exactly.
So OK.
We saw that with Python, essentially
you just kind of write things down
and you can run it, because
it's an interpreted language.
And so I propose that we actually
start by modeling the board.
And let's actually put this to-do
list down here so we can refer to it.
COLTON OGDEN: So Lance Maker's
question is, are you printing
the state of the board on the terminal?
Or will you be rendering images?
RODRIGO DABOIN SANCHEZ:
So the board will just
be text based on the
terminal, just a simple--
as much as-- you see board.py,
main.py, plan.txt here.
You would just see like 1, 2,
3 4, 5, 6, 7, blah, blah, blah.
That would be the board,
kind of like in a square.
Just by default, I have it so
that when I clear my screen,
I have my directory listed, like
all the components and stuff.
But if you actually don't want
that, you can have that gone too.
COLTON OGDEN: Quite a bit easier to
start implementing something like that
too, compared to making all the
artwork you need to make otherwise.
RODRIGO DABOIN SANCHEZ: Yeah.
We saw yesterday how long it
took to make sprites and stuff.
COLTON OGDEN: And those
were simple sprites too.
And then making games like this,
you can focus more on the gameplay
so you don't have to spend as much
time on the resource development.
And games like rougelikes,
for example, are
very rich in terms of
what you can do with them.
But they're Ascii art.
So they take one thing, put no emphasis
on it, and then put a lot of emphasis
on everything else.
It's like pros and cons, you know?
RODRIGO DABOIN SANCHEZ: So
what I'm doing right now
is I'm just creating a class.
And we briefly talked
about that yesterday.
COLTON OGDEN: We've mentioned
object-oriented programming.
We haven't gone into tremendous
detail on the mechanics of it
with what inheritance
is, what polymorphism is.
RODRIGO DABOIN SANCHEZ:
Essentially what we
said yesterday was that writing
a class is kind of like writing
a blueprint for something.
So if you want to represent an object--
in this case, we want
to represent the board.
So we want to have some sort of
idea and code of what a board is.
We can envision that--
things that a board can do is make
moves, render itself, randomize itself,
check if it has matched a
goal state of some kind,
if we want to attribute a function
for the board to solve itself.
These are all things that a
board should be able to do.
So what I'm doing is I'm writing a
class to make the blueprint of what
a board is and what properties it
might have and what methods or actions
it can take, et cetera.
COLTON OGDEN: Essentially let's
us define a new data type, where
that new data type is, instead
of being an integer or a string,
it's something that's a little bit more
representative of something we might
find in the real world--
a board, for example.
In computer science, there is no
intrinsic data type that is a board.
But we can define--
RODRIGO DABOIN SANCHEZ: Exactly.
COLTON OGDEN: --roughly
speaking in an abstract way,
the different pieces of information and
the functions that we might associate
with a board, sort of lump them together
so we can think about boards versus
the numerical information
we would need to keep--
have a representation of that.
RODRIGO DABOIN SANCHEZ: And so I just--
I was kind of typing a little
bit while you were explaining.
And this is touching
on what we were talking
about yesterday about the constructor,
which is different syntax in Lua.
But essentially when you create a
board, you define it as a new data type.
And you actually want to--
In Python, there is such a
thing as an int, I guess.
They're a number.
And there's a difference between the
theoretical existence of a number
and then actually having the number 4,
like writing x equals 4 or something.
So we can have this
blueprint of what a board is,
but then we actually want to create one.
And then when we create one, we
want to attribute properties to it.
And so that's what the
constructor is going to be doing.
So this kind of may be a
little bit foreign syntax--
def underscore, underscore,
init, self is essentially
the syntax for starting to
define the constructor in Python,
where self is just referring
to the board object itself.
And so what we're referring
to here in the plan
is that we need a way to represent
the board and solve the board.
So the goal-- so the
solved state, if we want
to have it look something like this, I
propose making a 2D lists of some kind.
And since we know that we want to
print the board onto the terminal,
I'm just going to go ahead and
make the numbers just strings.
And the nice thing about this
is that I can preemptively
put a space between the number
because I know if later on I'm
going to have a number like 10,
that's going to take up two character
positions.
Whereas if I have single
digit numbers, they're not
going to be aligned in the same way.
COLTON OGDEN: Right.
RODRIGO DABOIN SANCHEZ:
So I'm preemptively
trying to make sure that they
line up by keeping them in text.
COLTON OGDEN: That's cool.
RODRIGO DABOIN SANCHEZ: So yeah.
This is me creating the board
right now in the solved state.
COLTON OGDEN: Kind of much
like creating sprites.
Speaking of that, V1Rani was
saying, those sprites yesterday.
Slytherin 2.
Because remember the snake?
The 2 looked like a snake.
RODRIGO DABOIN SANCHEZ:
Oh, yeah, yeah, yeah.
That was funny.
COLTON OGDEN: And Nacleric,
have you played Dwarf Fortress?
I have played Dwarf
Fortress-- not a lot, though.
It's got a pretty
tremendous learning curve.
Have you played Dwarf Fortress?
RODRIGO DABOIN SANCHEZ: I have not.
COLTON OGDEN: Have you
heard of Dwarf Fortress?
RODRIGO DABOIN SANCHEZ:
I am-- most of the games
I play are very few in number.
COLTON OGDEN: Gotcha.
RODRIGO DABOIN SANCHEZ: I play FIFA.
COLTON OGDEN: That's a
nice way of saying no.
RODRIGO DABOIN SANCHEZ: Yeah.
[LAUGHTER]
COLTON OGDEN: That's a
very political way of--
RODRIGO DABOIN SANCHEZ: I love games,
but I don't play them that often.
COLTON OGDEN: Yeah.
RODRIGO DABOIN SANCHEZ:
Well, that's a lie.
I play the same games a lot.
COLTON OGDEN: Are you
familiar with rougelikes.
Do you know what that is?
[INAUDIBLE]
RODRIGO DABOIN SANCHEZ: Actually--
well, do you want to explain?
COLTON OGDEN: So if I can--
I'll show you an image
here on a separate laptop.
So images.google.com.
So this is what a roguelike looks like.
It's kind of like--
have you ever seen an image like this?
Look something like that?
RODRIGO DABOIN SANCHEZ: It
kind of makes me think of--
what's it called?
Everyone plays it nowadays.
The World of Warcraft
or something like that.
I don't know.
COLTON OGDEN: Yeah, not quite.
Or like this, where everything just
kind of looks like Ascii artwork.
Have you seen the games like that?
RODRIGO DABOIN SANCHEZ:
This is all new to me.
COLTON OGDEN: Oh, OK.
Well, we can maybe talk
about it another time.
Dwarf Fortress is a type
of roguelike, sort of.
But it's much deeper
roguelike than most roguelikes
are and much more flexible and much
more catered towards building things.
But yes, to answer Nacleric
question, I have played it--
only a little tiny bit.
A class is a struct on
steroids says Andre.
Yeah, in a sense.
Kind of.
Depends on the language I think.
Bhavik Knight, why not use loops?
I'm guessing we probably are going
to be using loops at some point.
RODRIGO DABOIN SANCHEZ:
Yeah, they're probably
referring to me making this board.
COLTON OGDEN: Oh, yeah.
RODRIGO DABOIN SANCHEZ: I'm just like--
COLTON OGDEN: Yeah, that's fair.
Yeah, you could do it
with a loop, I guess.
RODRIGO DABOIN SANCHEZ: I'm only
ever going to have to do it once.
COLTON OGDEN: This-- you visually
can-- if you're a programmer,
you visually can see--
there's a point to be
made about visually being able to see
exactly what the data structure looks
like.
And so I actually agree
with this decision
to draw everything out with strings.
It's just a lot more readable.
RODRIGO DABOIN SANCHEZ:
It helps me visualize.
Now I can see this is
the shape of the board.
COLTON OGDEN: One thing
I probably would do
is I'd probably do that, just
because-- that it's perfectly aligned.
RODRIGO DABOIN SANCHEZ:
Yeah, that's true.
And it's--
COLTON OGDEN: A little bit like that.
RODRIGO DABOIN SANCHEZ:
Especially because alignment
is important in Python.
It won't care about this so much.
But if I don't have this indented
properly, it's not going to--
COLTON OGDEN: Oh, yes, yes.
An important--
RODRIGO DABOIN SANCHEZ:
It's not going to like it.
COLTON OGDEN: --thing
for those new to Python.
Indentation is crucial.
You absolutely need
layers of indentation
to let the interpreter actually
work and run your program.
RODRIGO DABOIN SANCHEZ:
Questions in the chat?
COLTON OGDEN: No, I don't think any--
so dunder proto, anyone
here got the reference?
I'm not sure what that
is a reference to.
And then MOBA games, V1Rani is asking.
I used to play League a bit and Dota.
Have you played MOBA games at all?
League or Dota?
RODRIGO DABOIN SANCHEZ:
League of Legends.
That's what I was thinking.
Not World of Warcraft.
No, actually.
COLTON OGDEN: Oh, I got you.
OK.
Yeah, a little bit.
A little bit.
Too many games in the world.
Way too many games.
RODRIGO DABOIN SANCHEZ: So
what I'm doing right now,
I created this solved
state for the board.
And what I did after that is, I
imported from the copy library
this method-- or this
function called deepcopy.
And the whole point of that is I
know that the board is ultimately
going to get shifted around.
And instead of writing this
all over again or doing a loop,
I just essentially figured I
would just make a copy of the goal
and then shuffle it later.
Because if we think
about this game, it's
not necessarily going to be solvable
from any configuration of the board.
So the way that I want to solve it is
I want to start from a solved state
and then make, I don't know, like
100 random moves in any direction.
That way, I know I'm starting
from a position of a solved board,
making legal moves only,
shuffling the board that way.
COLTON OGDEN: Right.
RODRIGO DABOIN SANCHEZ:
So in order to do that,
I just want the board-- which
is going to change eventually--
start off being in the same
configuration as the goal.
I don't want to change
the goal because I'm
going to need it to compare whether
the current board is the goal.
So that's why I'm making a copy.
And the reason I'm using deepcopy
method is because what it does is it
recursively copies lists that
also contain lists, for example.
So--
COLTON OGDEN: Just saying
self.board equals self.goal.
We just make a reference--
basically a pointer to self.goal,
and then he changes to self.board.
RODRIGO DABOIN SANCHEZ: Exactly.
COLTON OGDEN: Before you
change it to self.goal.
RODRIGO DABOIN SANCHEZ:
If I do something
like this, what Python
is going to understand
is that these two variables
are referring to the same--
COLTON OGDEN: Same data structure--
RODRIGO DABOIN SANCHEZ:
The same data structure.
So if I change one, the other
one will be updated as well.
And I want to avoid that.
So I'm basically making a separate
copy of the board from the position--
the starting position of the goal.
And I'm making sure that I am avoiding
the issue of having only references
to the lists within the list by doing
this deepcopy, which recursively
makes sure that I copy everything.
COLTON OGDEN: That's very smart.
RODRIGO DABOIN SANCHEZ:
Then I'm also starting
to track the location of
the empty space, which
I have just chosen arbitrarily
to denote with these underscores.
And I know this is 0, 1, 2, 3.
And then this is 0, 1,
2, 3-- so column and row.
Maybe we can have something
like max col and max row.
COLTON OGDEN: Rabin is asking,
deepcopy should be inefficient, right?
RODRIGO DABOIN SANCHEZ: Maybe
with a very large data structure,
but this is so small that I don't think
it's going to really make a difference.
COLTON OGDEN: Yeah.
This is a tiny data structure.
Making 1,000 copies of this data
structure wouldn't-- you wouldn't even
notice any speed change probably.
But if it were hundreds of thousands of
rows in a database type data structure,
then yeah, I could see that
being very inefficient.
RODRIGO DABOIN SANCHEZ: This actually,
semantically, I think is backwards.
Max row, max col. And I am going to
go ahead and have these constants be--
well, they're constant, be equal to
4-- even though I know the index is 3,
because I can imagine myself wanting to
loop things repetitively from 0 to 3.
And with the Python range function,
the parameter that you pass into it
is not inclusive.
So if I pass in a range of 4,
it's going to give me 0, 1, 2, 3.
And so it'll just be nice having
these constants for semantic purposes.
COLTON OGDEN: The Last Kek was saying,
why not just Control C and Control V?
For any self.goal, self.board.
Pretty funny.
RODRIGO DABOIN SANCHEZ: Oh, yeah.
[LAUGHTER]
That would accomplish the same
purpose, but programmatically, it's
not necessarily great design
to just copy and paste.
COLTON OGDEN: Dry principle.
RODRIGO DABOIN SANCHEZ: Yeah.
COLTON OGDEN: Very important.
RODRIGO DABOIN SANCHEZ: It's more
of a me being a stickler, kind of.
And so we won't worry about this yet--
not jump into the moves just yet.
OK.
So we have an array to
represent the broad,
an array to represent the solved board.
COLTON OGDEN: Last Kek,
we know you're joking.
We're just messing with you too.
RODRIGO DABOIN SANCHEZ: Let's see.
I maybe just want now to make sure
that everything's working so far.
So I may want a way to
print this to the screen.
And Python has another one of these
funky class names called repr,
for representation--
like how do I want this board to be
represented if I print it or something?
So it's just a list that contains lists.
So I can imagine doing
something like, for i
in range of the max
number of rows, for j
in range of the max number of columns.
Then I want to just print--
we called this self.board--
at positions i and then j.
COLTON OGDEN: Often, you'll see y, x,
x, y for 2D data structures as well--
RODRIGO DABOIN SANCHEZ: True.
Yeah.
We can do that.
It's the same thing.
But whatever.
COLTON OGDEN: It's fine.
i, j is also fine.
RODRIGO DABOIN SANCHEZ:
Then if you notice--
if I just open up the Python
interpreter right here,
if I print something as simple as hello,
I get knocked down to the next line.
But if I include this
additional parameter called end,
which denotes what I want at the end
of the print statement-- by default,
it's a new line--
you'll see that when I didn't have
it, it knocked me down one peg.
And here the prompt is the same--
in the same line.
So if I know I want to print the
column side by side, then I--
when I'm iterating, probably don't
want that new line behavior by default.
COLTON OGDEN: Oh, V1Rani was
mentioning you have spaces
in these double digit numbers here.
RODRIGO DABOIN SANCHEZ: Oh,
that's-- yeah, that's true.
Thanks for keeping up.
That would have been fun
to see after we printed it.
COLTON OGDEN: Yeah.
That one's fine.
The 9's fine.
You can put--
RODRIGO DABOIN SANCHEZ: True.
COLTON OGDEN: --a space there.
RODRIGO DABOIN SANCHEZ: Good point.
OK.
COLTON OGDEN: Thanks,
V1Rani, for mentioning that.
RODRIGO DABOIN SANCHEZ:
Thanks for looking out.
Yeah, sweet.
COLTON OGDEN: Aeroman
was asking, just joined.
What are we doing today?
We are doing Game of
Fifteen, a prior CS50 pset,
but we're doing it in Python.
Traditionally, we've done it
in C, at the Command line.
And then Rabin is saying, in JS,
we call the prototype methods
with underscores dunder methods.
So they call those funky methods
dunder repr, dunder init, et cetera.
I have heard that as well, yes.
RODRIGO DABOIN SANCHEZ: So
I'm returning an empty string
here because the repr function,
by default, must return something.
And so I'm not going to
store this value anywhere.
This is just-- if I don't
include it, Python will complain.
OK.
So right now we have
a board with a goal--
an initial starting board-- and
then we have a way to print this.
So in the main screen, let's
just do something like b equals--
we're probably going
to have to import this.
So from the Board, which is
the name of our Python file--
from Board, we want to import--
it's this kind of funky syntax because
the file name is called Board.py,
and so this is referring
to the file name.
And now what I'm importing
is referring to-- if I
want to import any methods or classes
or whatever, we have a Board class.
So we're going to
import the Board class.
And then here we can do something
as simple as b is set to--
we're going to define a Board.
And then we can print b if we
want and see what it looks like.
All right.
Unless I screwed something up, let's
see what happens if I run main.
Name "goal" is not defined.
Oh, LOL.
So that's what the self keyboard is for.
This is called self.goal,
and I just put in goal.
So thanks, Python, for
watching out there.
OK.
So interesting-- we have
1, 2, 3, 4, 5, 6, 7, 8.
And now these are kind
of jammed together.
So we might want to put some
spaces in between these.
COLTON OGDEN: Aeroman was
asking, what terminal is this?
I am new to Python, so
can you suggest where do I
get this terminal or platform
where you are writing this code?
RODRIGO DABOIN SANCHEZ: What terminal?
Oh--
COLTON OGDEN: Or platform.
RODRIGO DABOIN SANCHEZ:
I configured my terminal.
So by default, it didn't look like--
it wasn't purple and stuff.
But in-- let's see.
Do I have my other--
where's my-- here we go.
Come on, Atom-- not highlighting.
COLTON OGDEN: I mean,
what we can probably
say is that it's terminal with a
specific bashrc or bash_profile,
whatever.
I forget what it is that customizes
your Command Prompt in your--
we've talked earlier, 18.10 or
18.08 Linux-- or Ubuntu Linux,
something like that.
RODRIGO DABOIN SANCHEZ: Yeah,
I'm on Linux Ubuntu 18.10.
And so there's the bash dot-- or the
.bashrc file that helps you customize
all this.
And so I wanted different things
to be in different colors.
So I knew-- so the name of my
current directory is in blue.
And then my user name is in green.
Everything else is in purple.
And if I'm in a GitHub repo, it will
show me the branch I'm on in yellow.
But maybe that's a topic
for another stream--
how to customize a terminal or so.
COLTON OGDEN: V1Rani's asking, is
"self" similar to "this" in JS?
JavaScript.
RODRIGO DABOIN SANCHEZ: Is what?
COLTON OGDEN: Is "self" similar
to "this" in JavaScript?
RODRIGO DABOIN SANCHEZ: Oh.
Yeah, it's similar.
And-- oh, that looks
a little bit better.
In Python, the whole
point of it is that--
this is the wrong--
let's just close this.
In Python, the whole point
is that if you're in a class,
the class is a blueprint.
And so let's say we have
a blueprint for a house.
The house itself, if it has
some things that it can do,
it's going to refer to itself
using the "self" keyword.
The typical example for classes
that I've heard used is a car.
A car should have a color property.
It should have functions like
it can drive, it can break.
It can do all these things.
And so when you're writing a class
that maybe is going to represent a car,
you can say self.color because
if you instantiate multiple cars,
you can imagine you want one to be red,
or one to be green, one to be blue.
So the "self" emphasizes that you're
talking about the current instantiation
of the object.
So--
COLTON OGDEN: "This" is a bit
weird in JavaScript because--
I mean, I haven't
stated this in a while,
but for example, every function
has its own "this" scope.
So it's a little bit weird.
If you call "this" within a
function that's within an object,
"this" is actually the
function and not the object.
I believe that's the case.
I ran into this issue fairly recently.
And the chat was talking
about how they're having
issues with "this" in JavaScript.
And yeah, it's a bit weird in ES5.
The "self" in Python
is much more consistent
and I think easy to understand.
But yeah, not arrow functions.
Yeah, arrow functions
don't have that problem.
That's correct.
RODRIGO DABOIN SANCHEZ: All right.
So so far, so good, right?
We have a 2D array to
represent the board.
So maybe we can get rid of
this and put this over here,
or something like-- things we have.
And then we have a way to
represent the solved goal.
So let's put this on here as well.
We have this location variable
to keep track of the empty tile,
although we haven't used it yet.
We have a function to display
the board on the screen.
OK.
So this might be a reasonable
time to look for a listener.
And what I'm going to do
right before that is--
you can have the main--
or whatever Python file
that you want to run,
you can have it run just kind of
without having to do any main methods
or anything like that.
But it's annoying if you want to
have different functions that run
it different-- like at specific times.
Because anything that you have, just
in that outermost layer right here,
is always going to
run by default. And so
if you want to wait to
execute some things,
it might be favorable to
put it in a main function.
So what I'm going to do for now is I'm
going to just create a main function,
def main.
And then all it's going to do for
now is print the board, I guess.
I want the board as a
global variable for now
in case I want to refer to it outside
of the main function at some point.
And then if you have a main
function, you have to--
I mean, you can do something as
simple as calling main right now,
but the convention in
Python is to write this
more cryptic looking statement called if
name equals equals main, then run main.
And what this does is,
essentially if you're
importing this file
to a different file--
so if this is going to be a
helper file for some reason,
it won't execute the code in that one.
And if you're just in this
one importing other stuff,
it knows that this is the main file.
And so it's going to
call the main function.
But we usually just wave
our hands at the syntax.
All we know now is that this should,
in theory, accomplish the same thing
as before, which it does.
OK.
So now let's look for
a keyboard listener.
And the one that I was toying
around with is called pynput.
And this is for those who may not
have ever heard this before, I
wanted to point at the documentation.
But it's just this small, little library
that supports listening for the mouse
and then listening for the keyboard.
For the purposes of this
project, I guess we're not
going to be doing much with the mouse.
But it does have a way to monitor the
keyboard and control the keyboard.
And then it handles errors and such.
COLTON OGDEN: TheLastKek
says, got to go.
CS50TV.
Love the stream.
Cheers.
Thanks, Last Kek, for joining us.
RODRIGO DABOIN SANCHEZ: Thank you.
COLTON OGDEN: See you
on the next stream.
Andre is saying, JavaScript's
"this" is a bit strange.
But yeah, it is.
In ES6, they made it
a little bit more, I
think, consistent with
other programming languages.
And to Andre's point, in C-like
languages-- like, for example, Java--
this is a bit more
like "self" in Python.
And then Aerospace Man--
or Aeroman123 was saying,
I'm working with a
supercomputer in Barcelona
running aerospace aerodynamics and
CFD simulation using open foam.
And then people were saying,
oh, that actually makes sense.
That's why your name is Aeroman123.
JS is lexcially scoped,
but when you want
something similar to dynamic
scoping, "this" gives us that.
On arrow functions, there
is no "this" defined
on the scope, so it goes up the scope
and attaches itself with parents "this"
definition.
I have to reread on all
that for the JS stream.
That should be coming
up in a couple of weeks.
And then Putevoditell was a new viewer
from yesterday-- says, hello, everyone.
Tomorrow there will be
a stream because today I
will not be able to be on
this wonderful channel.
Well, I hope you can join
us for tomorrow's stream.
Glad to have you with us.
Yeah.
Think we're all caught up now.
RODRIGO DABOIN SANCHEZ: All right.
COLTON OGDEN: So while I was
doing that, you programmed the--
RODRIGO DABOIN SANCHEZ: Well, no.
It's-- they-- in the
documentation, they have, hey,
if you want to control
the keyboard, for example,
this is how you press and release
keys or whatever manually.
But if you just want to listen for
the keyboard, you just copy this code.
You have to import the library, I think.
I don't think it comes by
default in the terminal.
I think I had to install it with pip.
And then once you have it, you
can import the keyboard method.
And all that it has is
kind of similar to Love2D
now, where you're listening
for when people press keys.
It has a method that fires when people--
or when someone-- a user
presses a key and one
that fires when they release that key.
And here's kind of the
actual listening portion.
And if you read through it, you
see that you pass in the methods
that you're going to be using.
So they're going to be using
an onPress and onRelease.
And what I did was, I just
kind of put this in our file
where I imported from py
input, the keyboard function.
I defined the onPress
and onRelease key exactly
as they had in their documentation.
And then in the main function--
in the main method, I
started the listener.
So that it's constantly listening.
And--
COLTON OGDEN: It takes the
place of like a, while true.
RODRIGO DABOIN SANCHEZ: Yeah.
Yeah.
And the only other thing is
that in the documentation
they say the way you can stop
the listener, you can pass it--
you can basically broadcast
some special codes.
But more simply, you can return false.
So in this default behavior
that they have here,
if what the user press was the
Escape key, they just return false.
And so if we want to just run their
code and see what that looks like--
I press Enter to execute
the file in the program
and its realized that I press Enter.
So if I press Up--
the terminal has this kind of annoying
behavior where if you press some keys
it gives you this annoying thing.
But here's their feedback
key.up has been released.
If I press a alphanumeric
key, a is pressed--
and the terminal has this
annoying echo, so we're
going to want to silence
that during our program.
Because we won't want it to display what
key we're pressing every single time.
But yeah, I can just do
something as simple as Escape now
and it says Escape
key has been released.
And if we take a look at the code,
that is exactly what this does
is whenever you print, whenever you
press a key, it just tells you such
and such key was pressed.
If it was alphanumeric, and
then if it was not alphanumeric,
it tells you that it was a special key.
And you know, it just
prints which when you press.
And when you released, it
tells you that you released it.
And then if what you
pressed the Escape key,
it kills the listener, which
basically terminates the program
because that's the last thing
happening and our main method.
So yeah.
Now what we want to do is adapt this
so that we can essentially have moves
that we can make in the game.
COLTON OGDEN: It's kind of like
Love2d, like you're saying,
it gives you the ability to like
check basically, oh, it's constantly
looking for input, sort of almost like
behind the scenes, but in this case
we have to [INAUDIBLE]
to find that behavior.
And then if you press-- if it
checks for the specific code,
and if that code is equal
to escape, or whatever,
you can decide to quit
the game, or you know, do
some sort of movement of the-- to
change some value of your data structure
to actually make the game playable.
And also, thanks Putevoditell
for the host, much appreciated.
RODRIGO DABOIN SANCHEZ: So
now we can basically just
get rid of some of the
stuff that they were doing.
And then I don't know, here we can--
we want to do something I guess.
So we can print, hello for
now, just as a placeholder.
And what we want, is whenever the user
presses the Escape key, that seems
reasonable to want to stop the program.
So we can also-- we'll
probably want to be listening,
if we look at the board,
we imagine the empty space
as kind of what we're going
to control moving around.
Let's say that we want the behavior
where if we press the Left key
the empty space swaps with
the number to its left.
So here we would swap with 15.
And then if we press Up, it goes
to 12, and so on and so forth.
So we haven't actually implemented
moves in the board yet,
but what we can do for now to
make sure that this is working,
is just print like left, or
print up, and right to make sure
that we have something working.
So--
COLTON OGDEN: Sanity checking.
RODRIGO DABOIN SANCHEZ: Right.
Sanity checking.
So right now I'm going to say,
if keyboard. key up is pressed,
I'm just going to print, up.
And then even though, you know, ideally
you're not supposed to copy and paste
code, sometimes you just gotta.
COLTON OGDEN: Who suggested that
up above, I forgot who that was.
That was, oh, that was
TheKek, TheLastKek, who
did that, which is funny.
RODRIGO DABOIN SANCHEZ: And here we go.
So in theory now, if we press
up, right, down, or left,
the program will tell us
what key we've pressed.
And then, later on, we'll
replace this with actual behavior
of moving in the board.
And--
COLTON OGDEN: It is an important step.
Make sure that you're actually
checking for the right things.
And that it's triggering that--
sort of like penciling
in your application.
Like filling in, like that, your
sketch with the actual color,
or the actual paint, or however
you want to analogize it.
But we do that even when we
program games to prevent stream.
Like a typing game, we did that.
We put all the labels and all the
text everywhere with like mock data.
And this is something like people
in web development do a lot too.
They'll make a static-- if they're
like trying to make a dynamic web page,
they'll put everything that's
static, that would represent what
the website would look like ultimately.
And then actually flesh it out
with the JavaScript, or the Ajax,
or whatever that they would normally
do to make it actually truly dynamic,
or whatever.
RODRIGO DABOIN SANCHEZ: Did I miss
anything in the chat or anything?
COLTON OGDEN: No.
No.
No.
No.
RODRIGO DABOIN SANCHEZ: OK.
Yeah.
So, so far we have what we
were talking about before,
the annoying echo that we're
going to have to get rid of.
But we see now, that when I was
pressing the up key, it was printing up.
When I was printing left,
it was printing left.
Down is pressing down.
[BELL DINGING]
Right it was printing right.
COLTON OGDEN: Sorry, if that was loud.
I forgot to mute my phone.
RODRIGO DABOIN SANCHEZ: For
now, let's go back to the board.
And we will probably want to implement.
So we have a listener
for key keystrokes,
now we want a function in the board
that will allow us to make moves.
So we kind of alluded to this
earlier how we want to create moves.
If we're taking a look at the
board, if I press the left,
I'm going to want to swap
with the element to my left.
If I press up, I'm going to want
to swap with the element above me,
and so on and so forth.
The only tricky thing is, even
though this looks like a square
this isn't necessarily a square memory.
This is, if we recall, and we'd
find this, this is a list of lists.
So we will have to be careful with what
indices we're adding and subtracting
when we do this.
But I propose that we start--
it's going to be very repetitive if we
have very similar behavior for making
a move to the left, very similar
behavior for making move up,
and the only thing
that's changing is, you
know, whether you're
adding to the column,
or you're adding to the row index.
So what I propose is having
just a general function
for making a move where we're
going to have some place
holder variables, x and y.
So let's do that.
Let's create a move function.
It's going to have to
take in itself again.
Ideally-- we could do this in two ways.
We could have it refer
to self.board every time,
but later on, if we want to make a
kind of like artificial intelligence
solve function, that
solves itself, that's
going to be annoying
because we're going to have
to search different possibilities.
And so if we start off with an
initial composition of the board,
we're going to want to
basically make four copies of it
because we can move
up, right, down, left.
And if the move function is
always referring to self.board--
like if we move up, then it'll
update self.board for everything.
So I propose that we allow a
parameter in the move function
where we can just pass in whichever
board we want to make the move on.
And then in the main .py file, we can
just pass in the objects board for now.
OK.
So we'll also want to keep track
of the location of the empty space.
And then since we know
that the way we're
going to swap elements
in our list of lists
is by adding and subtracting
to the indices of the list,
we'll want to pass in like an x
and a y, like you were saying.
This seems like it
should be comprehensive.
And if we want to just test out, you
know, how does swapping work in Python?
If I open up the interpreter
again, and I have--
usually what you would imagine
is you would have something
like, you know, x = 1, and then y = 2.
And then, if you want
to swap x to be y, you
would have to have some sort of
temp variable, maybe set to 0.
And have something like,
temp = x and then let's see--
COLTON OGDEN: x = y, y = x.
RODRIGO DABOIN SANCHEZ: x = y.
COLTON OGDEN: y = temp,
sorry. x = y, then y = temp.
RODRIGO DABOIN SANCHEZ:
And now y = temp.
And so what this would do, is now if
I see what x is, x has 2, y has 1.
But in Python, we actually
don't need this middleman.
Because we can do something
as simple as this--
if I have x = 1, and y = 2, we
can have something like x,y = y,x.
Which is very, very nice.
COLTON OGDEN: Yeah in
Lua2, which is super cool.
RODRIGO DABOIN SANCHEZ: In Lua as well?
COLTON OGDEN: Yeah.
RODRIGO DABOIN SANCHEZ: So yeah, now we
have x having the previous value of y,
and y having the previous value of x.
So that's just how swapping works in
Python, for those who didn't know.
That's going to be something that
we'll probably want to do here.
And let's just go
ahead and jump into it.
So we know that we're
going to be accessing
a board that's a list of lists.
So we want board@--
and when we move--
let's go back here.
When we move this tile we're
starting off at the position--
the empty position that we have
at which we're passing in here.
So let's say-- and the way that
we define the empty position
is, by default, the board
is going to be at 3, 3,
this won't matter when we shuffle it.
But we were going to have the
location of the empty space tract
with this variable, so
we're going to have--
on the x, it's going to tell
us what index in the row it is.
So like, this is row
0, row 1, row 2, row 3.
And the y component it's going to
have what index in the columns it is.
So this is 0, 1, 2, 3.
So we're going to want to refer to
the location at position 0 for the x.
And then the empty location
at position 1 for the y.
All right.
And this is going to be the empty slot.
So this is basically
like our first thing.
Then the next thing
that we want to swap,
is either the tile above it, to the
left, to the right, or below it.
So since we want this to be a general
move function where later we'll
have something like def move up, and
then def move right, and def move down,
and def move left, where all these
essentially just call the move
function with some parameter, some xy.
What we want to do, is if we're adding--
if we're moving up,
we will want to add--
or no, we will want to subtract
1 from the rope position.
And if we want to move
to the left or y, we'll
want to subtract or
add to the y position.
So we can do this--
we can do all this logic of whether
it's supposed to be minus or plus
and which one is supposed to be,
in these abstracted out methods,
such that in the-- so move
up would look something
like what we would pass in
the board and the e_loc.
And then we would pass
in something like--
if we're moving -1,0
something like this.
So here in the actual move
function, we would only
have to worry about adding x and y,
whatever they may be, to the index.
So this is going to look
something like e_loc 0+X.
X could be negative or positive.
And then e_loc at position 1 + y.
All right.
Now we're running out of space.
So this backslash in Python, let's
us kind of move on to the next line
without having to worry
about the end of a thought.
OK.
So here, essentially,
we have on the left hand
side of the swap, the position of the
board that contains the empty space.
On the right hand side,
we have the position
of the board that is, you
know, some relative distance
from the empty space based on
whatever we input to be x and y.
So if in like this move up example,
we're putting in -1 for x, and 0 for y,
that means that moving
up this right hand side
will be the empty
location -1 at the same y.
Which if we look at this--
if this is empty location 3, 3--
empty location -1 will be, and the x
component will be 2, so it's a 0, 1, 2.
And then, it's same y component.
So that'll be the same.
That'll be the 12.
So yeah.
That looks like we have about right.
And so now what we essentially
want to do, like we did x,y = y,x.
Is swap this.
COLTON OGDEN: Reverse them.
RODRIGO DABOIN SANCHEZ:
Reverse the order.
COLTON OGDEN: Virani was
was saying, "I just learned
how to construct dynamic web pages
with Express and EJS templates,"
That's awesome.
We'll probably actually have a
stream on that in the near future
after we get past the CSS, JavaScript,
Bootstrap, jQuery, all that fun stuff.
We'll probably go into
Node.js, and therefore Express.
Bhavik_Knight, in reference
to your temp swap example,
with saying the temp = 0 line was
actually redundant. in way I think.
Yeah, because you could just
do temp = y or whatever.
But I think you were just trying
to illustrate that like temp--
I guess maybe in reference
to other languages
where it would have a temp variable,
I can see you would usually send it
to something by default.
And then he was asking,
"can we do nested
class within the board?
Like a class move with up, down,
right, left, that inherits from move?"
RODRIGO DABOIN SANCHEZ: I don't
remember if you would put it inside.
I know you can definitely
do inheritance.
But I don't recall having seen
an example of a class written
within a class like that.
COLTON OGDEN: I think you can.
It's been a while since I've
actually needed to do that in Python.
I don't think I would do
it in this example though.
I don't think abstracting the idea
of a move really saves as much.
RODRIGO DABOIN SANCHEZ: OK.
So far so good.
And then what we did here was we update
the position of the empty location.
Because if we just swapped it-- so let's
pretend this is what we're swapping.
We swap position 3, 3 with
position 2, 3 If we move up,
we want the location of the empty
space to be updated as well.
We don't want to refer to 12
as the empty location now.
So we're also adding to the--
and this is kind of syntax
for like updating something.
So like if I opened up the terminal
again, Python, and then I have x = 1,
I could do something
like x = x + 1 to get 2.
But I could also do x + =
1 to add 1 and update it.
So now it's 3.
So this + = essentially means add,
you know, whatever it is to it,
and then update the value.
So what I'm doing here now
is making sure I also update
the position of the empty
space with the x and the y.
The only other thing that
comes to mind-- and then,
of course, I'm returning the updated
board and the updated location.
The only thing that comes to mind,
is if we look at this example
again, where we're in this
configuration, even though it's solved,
we could imagine that
the users in a corner.
So like the empty
location is in a corner.
And then, say, it's at
the bottom right corner,
the user tries to move
it to the right, or down,
and those locations don't exist.
So--
COLTON OGDEN: We, had that [INAUDIBLE]
yesterday with Minesweeper too.
When we were doing the
pre-calculation for all of the nodes.
RODRIGO DABOIN SANCHEZ: It would
be an easy thing to miss out.
Like to just forget to code it.
And then, you know, something breaks.
So before we actually do the
swapping we should probably check.
So yeah, swap update, empty location.
And then up here we're
going to check legality.
You know hashes are how
you comment in Python.
So the way we can do this is if the
empty locations, the x component
plus x, because x could
be positive or negative.
So if this is less than
0, or if, but whoops,
you don't have to write, if, twice.
If the empty locations,
x component plus x,
is greater than 3, because
0, 1, 2, 3, that it's the 4,
so if it's greater than 3, or if we have
something similar for the y component
plus the y is less than zero, or
it's less or greater than 3 + y.
COLTON OGDEN: It's arguable that
like a namedtuple might have
been appropriate for the location too.
So instead of having to index and
a 0, you could just do like .x, .y.
RODRIGO DABOIN SANCHEZ: True.
Yeah.
We haven't really used it much yet so
you may as well go ahead and do that.
We can have this be like--
or even a dictionary right?
COLTON OGDEN: Yeah.
A dictionary would work too.
RODRIGO DABOIN SANCHEZ: Be like row.
And then this could be column.
And then here--
COLTON OGDEN: So maybe just replace
every 0 with, row, or column.
RODRIGO DABOIN SANCHEZ: Ans
this will be easier if I just--
yeah, this semantically, is
probably easier to read as well.
COLTON OGDEN: I would've probably
done the namedtuple with .x.
and you could do .row as well.
Just to save like readability.
Just because it gets a little bit
cluttered here with all these brackets,
square brackets.
I would've probably just made an
empty location and namedtuple.
And I have to remember
the exact syntax for it.
Because I don't find
occasion to do it a lot.
But there is a--
I don't actually remember if
they made it a native 3.7 or 2.6,
just like part of the core language.
Because normally you have to--
here it is, namedtuple.
So you make a namedtuple,
call it, in this case,
you could just call
it whatever you want.
Which takes in and has
an x and y, like that.
And then from every--
every time you want
to do that afterwards,
you can just say like right here, see
how this is p.x and p.y and you would
have the same-- that would be
e_loc and then e_loc.x and e_loc.y.
It might be a little bit
too much at this point
to go back and
retroactively do all that.
Especially if you're like--
you've mentally already constructed
it with the 0,1 index model.
But it's a nice thing that
they've recently started.
It's recently started becoming
pretty trendy with Python.
RODRIGO DABOIN SANCHEZ: Yeah.
I can see how that would be pretty nice.
Yeah.
So we could--
COLTON OGDEN: You can revert, if
you want, back to the old 0, 1,
if that's easier.
RODRIGO DABOIN SANCHEZ: I could see the
semantic value of doing it this way,
but I mean, you know, if we're--
COLTON OGDEN: I wouldn't blame
you for- I wouldn't blame you.
It's kind of like a brand new like,
wrench to kind of throw into it.
So if you want to--
if you want to keep it
as it was totally fine.
RODRIGO DABOIN SANCHEZ: But I
mean-- so here we are again.
Wait, I mean, the first location--
so the 0 is referring to the rows,
and the 1 is location to the columns.
OK.
So we're back where we started.
And here we have the
position of the board,
of the empty location here
where we're swapping to.
And then we're changing the position.
We're updating the position
of the empty location.
We're returning the updated board
in the updated, empty location.
Here we're checking
if the move is legal.
So if basically we've
checked if adding x or y--
x or y to the x and y
components of the empty location
exceeds, or like goes through
an index that doesn't exist.
We don't want to do anything.
So maybe we just returned the
board and the location as it is.
COLTON OGDEN: "Guys, are you
planning on about explaining
advanced concepts in Python,
and things like decorators,
comprehensions, generators,
in one of your future streams?
You guys have a great teaching skill."
First of all, thank you very
much for the compliment.
We would definitely like to do
a more advanced Python streams.
Veronica is, per what
Bhavik_Knight said,
doing a stream in the
near future on and more--
well, the stream is called, "A Hundred
Cool Things in Python, Part 2".
We already did in Part
1, where we covered
about 40 different small things.
We'll probably do that
again with a future stream.
No concrete plans on all
of those necessarily,
but doing a more structured
advanced Python stream
would be pretty fun and pretty cool.
So we'll little different
take a look at doing that.
So thanks for the suggestion.
RODRIGO DABOIN SANCHEZ: Now that
we have this general move function,
I'm trying to go ahead and update
these to look how they should.
So if we know the move
function is returning
the updated board and
location, we'll also
want to return whatever this returns.
And now we'll have to change the
x's and y's for this each time.
OK.
So moving up, that's
subtracting 1 from the row.
Moving to the right is leaving the
row alone, adding 1 to the column.
Moving down is adding 1 to the
row, leaving the column alone.
And then moving left is leaving
the row alone, and subtracting 1
from the column.
So in theory we should have
a working way of moving here.
OK?
So now that we have this, we can
probably update in the main .py file.
What happens when we release the key?
The only other thing, though, is that
even if we override this behavior
to actually make the moves, we
are only ever printing the board
at the beginning of the main function.
So we wouldn't actually
see anything change.
So we might want to write a function
for the board that just kind of like
refreshes the terminal
screen and re-displays it.
Something like def refresh.
And for this, actually,
if we remember what this--
what the terminal
looks like when we were
writing the left key, the down key, the
up key, it would give us a little echo.
So if we actually
import up here from OS--
let's import system.
So that we can clear the
terminal screen programmatically.
That way we get rid of
whatever it was previously.
And we can just see the latest
version of what we have.
We can have in the refresh
method that we're going to write,
we can have the board class
programmatically clear the terminal
screen so that in the un-press,
instead of printing hello,
every time that we press anything--
whoops.
Let's put this here just for my sake.
Any time that we press the key,
we will call the refresh method,
so that it will clear the
terminal, get rid of any
echo that the terminal is
going to display for us.
And then here, after
whatever we do, we're
also going to call the refresh method
so that after we press up, or down,
or right, or left, it will re-display.
So what this will do, is
it will clear the screen,
and then we'll want it to
display the board itself.
So we already have a function
that prints the board,
so we can just print self.
All right.
And maybe what we can do since--
yeah.
This is probably sufficient.
So here we have, after we release
a key, we're clearing the screen,
printing the board.
Whenever we press a key, we're clearing
the screen, printing the board.
I guess we don't need to
listen to, print this here,
but we'll leave it for now.
All right.
And now the only thing that's missing is
actually writing the moves to the keys.
OK.
All right.
So here, what we'll
want to do, is we have--
we'll want to-- if we take a look
at what our move function does,
is it returns the
board and the location.
So what we want to do is call--
and so it does this return
the board and location.
So we'll want to say b.board, b.--
and it's actually kind of confusing
because I named this, empty location.
So let's just have this be the
same name so I don't get confused.
OK.
So b.e_loc, this is going to take
the value of calling b.move up.
And it's going to take in the
board, the empty location,
and I think that's all that it needs.
Yeah.
Because we took care of the rest.
All right.
Just for incremental testing, let's
see what happens when I run this.
All right.
And e_loc is not--
COLTON OGDEN: And crash.
RODRIGO DABOIN SANCHEZ: --defined.
Ha ha!
Perfect.
Where do we have e_loc
not being defined?
Oh!
Where is-- here.
I named this just Loc instead of e_loc.
So I'm trying to use a
parameter that doesn't exist.
All right.
COLTON OGDEN: That
will mess you right up.
RODRIGO DABOIN SANCHEZ: Now, we actually
have the parameters named properly.
And here, I actually specify
the right names, I believe.
OK.
Let's try this again.
All right.
So we notice when I press Enter,
the whole screen was cleared
and now we just have the board.
So it's the foc--
the center of attention.
OK.
There we go.
So we pressed Up key, and
we have been able to move.
COLTON OGDEN: Nice.
RODRIGO DABOIN SANCHEZ:
I'll press Escape
to quit so that we can
finish implementing
this in the other directions as well.
All right.
COLTON OGDEN: It's
nice CLI interface, all
things considered too,
rather than having
like, you press a key to say I want to
move up, and it has a menu or whatever,
and then it like reprints the board
below that, refreshing the CLI.
RODRIGO DABOIN SANCHEZ: I think the way
that the old Pset and CS50 used to be,
was that it would prompt you.
Do you want to move up, left, down,
and right, and then you would type it.
And then it would reprint
the board below that.
COLTON OGDEN: Yeah, exactly.
RODRIGO DABOIN SANCHEZ:
It was kind of annoying.
COLTON OGDEN: A little more of
a traditional way of doing it,
I think, but also easier to understand.
Because you don't have
to go through like,
you know, refreshing in the
terminal, doing asynchronous
input, that sort of thing.
Right?
RODRIGO DABOIN SANCHEZ: We
can actually take a look
at what it looks like if we
don't refresh on key press.
So if I, you know, if I just
don't include this, I think,
pass keyword makes it do something?
COLTON OGDEN: Pass
keyword will do nothing.
Correct.
RODRIGO DABOIN SANCHEZ: And so up
is up, right is right, down is down,
left is left.
OK.
So if I don't refresh the terminal,
we get something like this--
COLTON OGDEN: Oh.
OK
RODRIGO DABOIN SANCHEZ:
Where every time that I
press a key, you see like down is b,
up is a, left is d, and then right
is c, for some reason.
So the terminal is
always printing this if I
don't clear the screen on key press.
So that's why I wanted
to just preemptively do
that so we could avoid that.
And also, obviously, if I
don't clear the screen here--
so actually let's just do this.
Let's get rid of this comment now.
And if, within the refresh method
itself, I don't clear the screen
and we run it--
COLTON OGDEN: Wasn't this
easier in the Game of Fifteen,
asking for which number
you wish to move?
There's only one empty
field on the board.
RODRIGO DABOIN SANCHEZ: That's true.
COLTON OGDEN: There's no ambiguity.
RODRIGO DABOIN SANCHEZ: That's what it
was, which number did you want to move?
And so the way this
one is going to work,
is you just press with the arrow keys.
You move the--
COLTON OGDEN: I like
that because it's nice.
You don't have to think too much about--
RODRIGO DABOIN SANCHEZ: And you
can do it more quickly as well.
COLTON OGDEN: Yeah.
Much more quickly.
RODRIGO DABOIN SANCHEZ: And
here, we're not refreshing.
They're like, we're not
clearing the terminal.
So we get all this junk.
Right?
So that's kind of a headache.
So that's why we want to go ahead
and clear this anytime that we
press a key so that we don't have to
see, oh, which key did they press?
And also, what did the previous
board look like, or whatever?
OK.
So, so far so good.
And just, you know, just to confirm that
we have the behavior that we wanted to.
Oh, you know what?
I don't like that.
I don't like how when
you run the program--
py main.
You have that kind of
like visual thing where
it like the prompt was there, and
then the board, and then it was gone.
So at the beginning of the main
method, let's just go ahead
and instead of printing b,
let's just call it b.refresh.
COLTON OGDEN: Nice.
OK.
RODRIGO DABOIN SANCHEZ: OK.
So now as soon as we run the
program it'll clear everything.
OK.
That looks better.
COLTON OGDEN: That looks better, yeah.
That looks nice.
RODRIGO DABOIN SANCHEZ:
We should probably
have something like, I don't know,
it just seems kind of lonely.
Let's say something like, print,
welcome to the game of fifteen.
And maybe that'll look
a little more friendly.
OK.
COLTON OGDEN: Cool.
RODRIGO DABOIN SANCHEZ: We should
probably put a space in between.
COLTON OGDEN: Yeah.
Probably.
But it's nice.
Nice little touch.
If you were really pro, you
would have like game of fifteen,
and like big ASCII art.
RODRIGO DABOIN SANCHEZ: Yeah.
COLTON OGDEN: You could
get like-- you can get--
I think there's websites
where you can copy and paste
ASCII art if you like
type it in, they'll
give you a text string you can copy.
That'd be pretty cool.
RODRIGO DABOIN SANCHEZ:
So we essentially
have a workable version right now.
If I'm at the left most corner and
I keep pressing left and nothing's
happening, if I'm at
the right most corner
and I keep pressing right nothing's
happening, same if I'm down
and I'm pressing down,
nothing's breaking.
COLTON OGDEN: Nice.
RODRIGO DABOIN SANCHEZ: And if I'm up--
OK.
So--
COLTON OGDEN: Is the boundary locked?
RODRIGO DABOIN SANCHEZ: So far so good.
We're locked into our board.
We can't move outside of it.
We can make moves.
So nice.
We have basic board
function to make moves.
You know what I didn't write
in the things that we need?
We need a way to randomize the board.
COLTON OGDEN: There we go.
Yeah.
Because then they're going to start off
every game and it's going to be solved.
RODRIGO DABOIN SANCHEZ:
It's already solved.
Yeah.
OK.
So that seems like a good candidate
for another board function, or board
method.
You know, functions and methods
are essentially the same,
except methods are
functions inside of class.
I think that's the distinction.
COLTON OGDEN: Yep.
RODRIGO DABOIN SANCHEZ: So what
I alluded to at the beginning,
is a way that we want to
randomize the board such
that it's solvable is, we want it
to start from a solid configuration
and then make n number
of moves to randomize it.
So let's make another
pseudo constant here
and call it like, shuffle
magnitude or something.
And just to keep it simple so that,
you know, I can at least still
solve the game without having to play
for a considerable number of times,
let's just self shuffle it
like, I don't know, ten numbers.
COLTON OGDEN: One time.
RODRIGO DABOIN SANCHEZ:
Yeah, we have a 50% chance
of starting in a winning move because
it can be to the right or down.
COLTON OGDEN: Well yeah.
You could do boundary
constraints on your shuffles
such that it wouldn't consider it
a shuffle if it tries to move--
RODRIGO DABOIN SANCHEZ: True.
COLTON OGDEN: --outside of the bounds.
But--
RODRIGO DABOIN SANCHEZ: Yeah.
COLTON OGDEN: For the
sake of simplicity.
RODRIGO DABOIN SANCHEZ: I mean,
if we just make it, you know,
if we make the magnitude
large enough that I
don't think it will really matter.
COLTON OGDEN: Choose down 100 in a row.
RODRIGO DABOIN SANCHEZ:
What are the odds, right?
OK.
So we have the constructor,
the printing, let's just--
I don't like how it's all blue.
I'm kind of like weird
about some things.
Clear the screen and print the board.
There, now there's a
nice little separator.
See, now I'm thinking about it.
Now I've got to do it for all of them.
You know?
Represent the board.
And then here--
COLTON OGDEN: There are
some streamings where
I don't even comment my code because
I just completely forget to do so.
I try to be better about it.
But-- "Just get a linter to remind
you of that," says MKloppenburg.
Yeah.
It's a good point.
RODRIGO DABOIN SANCHEZ: Checks.
Let's just say, makes legal move.
COLTON OGDEN: The linters that are
integrated into the text editors too--
I can get one for--
I think you could definitely
get one for Atom and VS Code.
It'll actually show you on the left
side of the screen, in your file here,
if you have issues.
You can do like pep8 linters.
And you can also do like syntax
linters and things like that.
It's pretty cool.
RODRIGO DABOIN SANCHEZ: OK.
So we want this shuffle
function method that randomizes
board using succession of legal moves.
So that way, we know for sure
that it's going to be solvable.
All right.
We'll probably need the
random library, I guess.
Yeah.
So let's go ahead and import that.
What's it like from
a,b,c, d,e f,g,h,i,j--
COLTON OGDEN: I think
you just import random.
RODRIGO DABOIN SANCHEZ: No.
I like having them alphabetized.
Right?
Wait.
Harvard student doesn't know
the order of the alphabet.
Does r come before or after o?
COLTON OGDEN: Comes after o.
RODRIGO DABOIN SANCHEZ: Ah-ha.
See?
We're just human.
From random import, random.
COLTON OGDEN: I would say because
English is your second language.
RODRIGO DABOIN SANCHEZ: That's true.
COLTON OGDEN: I'll give you a pass.
But Spanish uses the same
alphabet, don't they?
Same order?
RODRIGO DABOIN SANCHEZ: Yeah.
But it has some different letters.
COLTON OGDEN: So you
don't get an excuse.
RODRIGO DABOIN SANCHEZ: It's a
total different thing, you know?
COLTON OGDEN: Kind of.
Maybe.
I'll give you that one.
I'll give you that one.
RODRIGO DABOIN SANCHEZ: So,
from the random library,
there's a function called randint,
that gives you a random integer.
And I think we talked
about this in your stream
yesterday also, that you should
generally seed your random number
generator so that you keep getting
different results each time
that you run it.
COLTON OGDEN: Which you want to
test specific corner cases, but yes.
RODRIGO DABOIN SANCHEZ: True.
Good point.
OK.
So I think, now if we
go back to our shuffle--
COLTON OGDEN: R.B was asking,
"In Ruby, the convention
is that you write code such
that you don't need to comment.
Is there something like this in Python?
As far as it goes, writing a lot of
comments in Ruby is bad practice."
RODRIGO DABOIN SANCHEZ: What?
COLTON OGDEN: It's a bit strange.
I mean, I'm not too involved
in the Ruby ecosystem.
What I imagine they're
probably referring to,
is writing self documenting code, in
the sense that your variables are clear.
RODRIGO DABOIN SANCHEZ:
Oh, yeah, So like I mean--
COLTON OGDEN: Function names are clear.
RODRIGO DABOIN SANCHEZ: We
couldn't name these anything.
Right?
We can name this like
self.tomoato and self.pear.
And, you know, it would've worked
as long as he kept track of it.
Can you tell him hungry?
No I'm just kidding.
COLTON OGDEN: We just had some
awesome curry katsu-- katsu
curry, which is pretty good.
Yeah, I think, I mean, I
wouldn't go so far as to say
don't comment your code at all in Ruby.
I feel like that's kind
of a silly convention.
Especially if you're
writing in algorithm
that's of moderate complexity.
I don't think any amount
of self documenting code
is going to make everything 100% clear.
Like, if you're doing, I don't know,
like, recursive maze generation
algorithm or something
in Ruby, I don't think
there's any amount of
variable naming and convention
that's going to save a programmer
from knowing exactly what's going on,
not knowing exactly what's going on.
So I would say don't
worry too much about that.
In Python, definitely use
comments when they make sense.
And I think this should apply
to most programming languages.
And I have to look into
Ruby and see why they're
saying that because I don't know
the ins and outs of the language
quite as much as I know
Python's as a language,
but my instinct is that they're
referring to variable names and some
of their syntax that sort of
makes it clear whether something
is a Boolean expression,
or a Boolean function.
Like the question mark that you can use
for function names and stuff like that.
But, yeah, I would say use comments
but make your variables and functions
and lay out of your code sort
of obvious what's going on.
As much as you can.
Right?
RODRIGO DABOIN SANCHEZ: Bhavik
is asking, "Magnitude of 100
won't necessarily mean that we have to
do 100 moves to solve the game, right?"
So yeah.
That's-- I mean you're completely right.
The magnitude that I'm
referring to, is like how many
moves are we going to make before
we present the board to the user
so that, you know, it's at least
not in the solved configuration.
COLTON OGDEN: It could
theoretically be 0.
Because it could
theoretically shuffle it,
and then, shuffle it all back
there and back to the beginning.
RODRIGO DABOIN SANCHEZ: Yeah.
COLTON OGDEN: It's not-- yeah.
It's--
RODRIGO DABOIN SANCHEZ: Like, I
mean, in a shuffle magnitude of 10,
it's not that crazy to
think that they'll move,
you know, up and then left.
But then they'll move down and right.
And then move up and then back down.
And that by the end of
the 10 moves they'll
end up right exactly where you started.
COLTON OGDEN: I don't
know how you would compute
like, just how many moves it would
take from that starting point,
that 100 magnitude.
Because I can visualize, like, the
algorithm kind of going back and forth.
And I can imagine there's
multiple ways, probably,
to move the things to get
it back in the same position
if it's like, kind of making
moves in the same area.
I don't know 100%.
But I would imagine
that that's probably--
it probably doesn't map anywhere close.
"Self documenting code is code
without comments," I wrote--
code without comment-- wait.
"Self documenting code equals
code without comments."
I wrote, "nasty spaghetti code
equals code without comments."
Someone else wrote, "True, true."
"Mathematically the game must maintain
its invariant to be solvable."
RODRIGO DABOIN SANCHEZ: Yeah.
That's why we don't want to just
randomize the whole list of lists.
Because then we might end up with
a configuration that can't actually
be solved.
COLTON OGDEN: Yeah.
That's true.
RODRIGO DABOIN SANCHEZ: OK.
So what I was doing initially
in the shuffle method,
is first I seeded the
random integer generator.
And then I thought to myself
something along the lines of,
OK, for 0, till however
magnitude is, I probably
want to pick a number
between 0 and 3, or 1 and 4,
and have that map to
one of these functions.
Right?
So like maybe getting
0 means you move up.
Getting 1 means you move right,
2 move down, 3 move left.
Then I realized I probably needed
some data structure to do that.
So I'm going up here and I'm creating a
data structure that does exactly that.
So the nice thing about
dictionaries in Python
is that the keys can be anything.
That can be like strings or
ands or whatever, what have you.
So I'm going to have a
dictionary called moves,
which is going to be like a dictionary
of legal moves that I can make.
And so if the key is 0, I'm
going to have it be self.move up.
I don't want to include
the parentheses because I
don't want it to actually execute.
I just want to kind of delay that.
COLTON OGDEN: Using function
pointers, basically.
RODRIGO DABOIN SANCHEZ: Basically, yeah.
And then I'm going to have 1.2.
And if you're curious about why a
mapping like random numbers to this,
it's like I think of up, right, down,
and left, it's kind of like north,
east, south, and west.
And so, just in my mental compass,
I start up here and then kind
of move clockwise.
That's why I'm going up,
right, down, left, 0, 1, 2, 3.
COLTON OGDEN: I go
up, down, left, right.
RODRIGO DABOIN SANCHEZ: You
go up, down, left, right.
COLTON OGDEN: Yeah.
I think it's because
up, down go together,
and then left, right go together.
And so I just see them--
RODRIGO DABOIN SANCHEZ: That's valid.
COLTON OGDEN: --sequentially.
RODRIGO DABOIN SANCHEZ: Yeah.
For me it makes sense to
go in the cyclical way.
COLTON OGDEN: I can I
can visualize that too.
RODRIGO DABOIN SANCHEZ: Oh, I forgot
to refer to the fact this is--
COLTON OGDEN: Rabine, I might take a
look at that style guide at some point,
and then maybe we'll do a
Ruby stream in the future too.
RODRIGO DABOIN SANCHEZ:
Sefl.move down and then 3
is going to refer to self.move left.
I cheated.
OK.
All right.
So that seems reasonable.
0 self, move up, 1, move right,
2 move down, 3 move left.
OK.
So now in the shuffle method,
I've seeded the generator.
I am going from 0 to
whatever magnitude I wish.
And then choosing a random
ends between 0 and 3.
And now I'm going to have
it do something like--
let's see, self.moves--
COLTON OGDEN: Add 0 to 3, inclusive?
RODRIGO DABOIN SANCHEZ: Yes.
Self.moves at i, and now I
want to actually execute it.
Remember that move up, move right, at
all, take in the board and the location
that we're going to update.
So I'll have to pass in
self.board here, and self.e_loc.
That's what I named it, right?
Yes.
OK.
COLTON OGDEN: I agree with Andre
saying, "Comments aren't necessarily
aimed at explaining what code
is, but why it's doing it."
Yeah.
And I agree that no amount of self
documenting code can help with that.
Like I said, you have
to understand what's
going on in the programmer's
mind and there are
algorithmic sort of map mental mapping.
Right?
Like that could-- you could
conceive of a problem abstractly
in a way that's not even representative
of any programming language construct,
but you have to use the programming
language to sort of create that.
Like creating a data structure,
using that data structure,
in a particular way.
Visualizing a problem
in a particular way.
The what versus the y,
that's essentially it.
Yeah.
RODRIGO DABOIN SANCHEZ:
All I've done now
is I've just made sure that the first
thing I do is shuffle the board.
So when we actually rerun this now,
I have a key error, which is great.
Not intended.
So in line 48 and shuffle, key error.
Oh, LOL.
I did not mean to do i, I meant to do m.
So what I was doing is I was
essentially indexing into moves as
long as the loop went from 0 to
whatever, and what I meant to do
was each time that the
loop iterates, m is
going to take in a random
value between 0 and 3.
And that's the position
that I want index into.
OK.
All right.
So-- I mean, obviously, this
is randomized in some way.
It doesn't look super random
because it's only 10 moves.
But if I do this like 1,000 times, then
that definitely looks more shuffled.
I don't really like that the blank
space is starting kind of anywhere.
And like it's fine.
Right?
We know it's solvable.
But just like us aesthetically,
I like the idea of it
starting in the bottom
right corner area.
COLTON OGDEN: Sure.
Yeah.
RODRIGO DABOIN SANCHEZ: So I'm
going to do something that's
not really necessary per say, but--
COLTON OGDEN: You mean, like move
the underscore to the bottom right
no matter where it is.
RODRIGO DABOIN SANCHEZ:
And so obviously, I
don't want to just swap its location
with whatever supposition 3,
3, because that could potentially
break the solvability of the board.
COLTON OGDEN: Right.
Yeah.
You have to actually move it
there, the same way you do it--
RODRIGO DABOIN SANCHEZ: Right.
So I'm going to just, you know,
at most, if we run this again--
so at most, I would have to
move it down 1, 2, 3 times.
And then let's say it started up here,
I would have to move 1, 2, 3 down.
And then 1, 2, 3, to the right.
So we could just write--
COLTON OGDEN: Are you
just going to call self.--
are you going to call move three
times to the right, three times down.
RODRIGO DABOIN SANCHEZ: Yeah.
So for i in range, like,
max number of rows,
I might just do self.moves down is 2.
Slef.moves at 2.
Self.board, self.e_loc.
And then, I'm going
to do the same thing.
Even though it's
syntactically the same thing,
semantically, you know, just thinking,
I'm going to move the columns.
And right is 1, up right.
COLTON OGDEN: So this is of like
the what versus the y, right?
Like this is a good example of
why comment would be useful here.
Like you know exactly what's going
on here, I mean, roughly speaking,
it's a little abstract.
But it's like, you
don't maybe understand
the reason for why they're doing
this, and so somebody in their comment
would be like, we want to make
sure that the underscore starts
at the bottom right every
time for aesthetic purposes.
Like I don't know how you're going
to communicate that necessarily
with just pure syntax.
RODRIGO DABOIN SANCHEZ: Mm-hmm.
COLTON OGDEN: Like that's a human sort
of aesthetic desire more than anything
else.
So yeah, that's to
Andre's point, I think
that's an example, a good example
as to illustrate using comments that
are based on the why, not the what.
RODRIGO DABOIN SANCHEZ: Right.
And even-- so I I've worked as a TF
for CS50, and I had to grade code.
And so previously, you
know, we have graded
on correctness, design, and style.
Where correctness is,
does the program work?
Style, is like, is it
formatted properly?
Does it contain any
sort of comments at all?
And design, is you know, how
efficiently was it written?
Some decisions that went into it.
But one of them, is like, how
useful are the comments, right?
Because the comments could
just be cluttering up the code
if they're meaningless.
And one of the things we say
is, if you're writing a comment,
don't just restate what you can
clearly read in the code itself.
Tell us like, what the what the
decision making process was behind.
Like why are you doing this?
So I could have easily
written a comment here
that says, you know, make three
moves down, make three moves right--
that's just kind of restating this.
But what I'm doing here is, you
know, I optionally, I just want to--
or maybe, for aesthetic
purposes-- for aesthetic purposes,
move the empty space to
a lower right corner.
So even though the code is legible,
you might be like, uh, OK, why?
But the comment says, oh, this is what
the person that coded this wanted.
COLTON OGDEN: You got to know what the
programmer is thinking when they're
writing the application so that you
can get into their frame of mind
and understand the problem
they're trying to solve.
RODRIGO DABOIN SANCHEZ: So now
we see that the board is shuffled
and we're starting in
the lower left corner--
lower right corner.
Yes.
English isn't my first language, so.
COLTON OGDEN: Yeah, left
and right, you know.
RODRIGO DABOIN SANCHEZ: OK.
Great.
And just third time--
three times is a pattern.
Is that the saying?
COLTON OGDEN: What is it?
RODRIGO DABOIN SANCHEZ: If you do
something three times it's a pattern.
Or like if something
happens three times.
COLTON OGDEN: Oh, yeah
RODRIGO DABOIN SANCHEZ:
Like once is an accident,
two is a coincidence,
three is a pattern.
Something like.
COLTON OGDEN: Yeah.
RODRIGO DABOIN SANCHEZ:
I don't know actually
if that's the saying in Spanish,
or if that's a saying in English.
COLTON OGDEN: Is that
a saying in Spanish?
RODRIGO DABOIN SANCHEZ: I have no idea.
I don't know.
OK.
So we're making some good progress here.
We have managed to randomize the board.
So all we have left is a function to
solve the game, and a game over screen.
Solving the game is going to be
a little bit annoying, I guess.
So we can leave that last.
Unless we kind of do
like a temporary, kind of
like, cheat, where we
say, hey, def solve self.
All we're going to do is say,
like, self.board = deep copy
of self.goal right?
Like this is kind of like
a dumb way to solve it.
Right?
Right now.
COLTON OGDEN: Hey, solved.
RODRIGO DABOIN SANCHEZ:
Or if we do this, oh--
COLTON OGDEN: It would
be cool if over, if like,
you have a timer that just kind of
like does the moves individually
for the solve algorithm or whatever.
RODRIGO DABOIN SANCHEZ:
Well, what I am hoping to--
I think we'll have time.
Is I want to write--
so instead of having this cheap kind
of like cheating solve function,
is replace this with Breadth-first
search algorithm that will
search for the correct path to take.
And then solve the game for us.
I think we'll have time to do that.
Because all we have left is, you
know, check game over screen.
And--
COLTON OGDEN: Oh, did you type
a self.board with an 'ed'?
You know what they're saying?
RODRIGO DABOIN SANCHEZ: Not really.
COLTON OGDEN: Oh, they
were saying something--
I think--
RODRIGO DABOIN SANCHEZ: Self.bored.
COLTON OGDEN: Wasn't
sure if they had it--
of you had written that
as a typo or something.
RODRIGO DABOIN SANCHEZ:
I guess I can check.
Well, I mean, Python
will tell me, right?
If I'm referring to a
variable that doesn't exist.
COLTON OGDEN: If you're assigning
it to something, though,
I wouldn't consider that
a syntactical error.
Right?
RODRIGO DABOIN SANCHEZ:
Let me control p.solve.
COLTON OGDEN: Or if you're
saying something to it,
it would just consider that
a new variable declaration.
RODRIGO DABOIN SANCHEZ: No.
I think they're trolling.
COLTON OGDEN: I think were trolling.
Thanks for the troll,
BlueBooger, we appreciate it.
RODRIGO DABOIN SANCHEZ: OK.
So we are going to test now.
So I just arbitrarily mapped the
solve function to the Shift key.
So now let's see if we
rerun this and press Shift.
OK.
So we have solved the game.
COLTON OGDEN: Good job.
RODRIGO DABOIN SANCHEZ: All right.
So we probably now--
it'll be a lot faster to just
implement game over than solve.
So we can do that ideally by
checking whether the goal is
equal to the board somewhere.
So I'm thinking we can have it be--
let's see, let me
refer to my notes here.
Something that we do
pretty often is refresh.
So, OK, here's a thought,
here's what past me thought of.
We can have it check--
since we know that the last
thing in our main function,
right, is the keyboard listener.
If we want the program to
end we have to return false.
We have to give this kind of
like signal to the listener,
return false, like, OK, stop.
So what we can do is,
since, what we always
do is refresh every
time we press a key, we
can include in the refresh method,
a check for whether the goal is
the same as the board.
And then have the refresh
method return true or false.
Where if kind of, you
know, counter-intuitively,
if the game is over, and like solved.
You return false.
So that the listener quits.
Otherwise, you return
true, so that it continues.
So let's do that.
OK.
Let's go to refresh, here.
And let's have it do this--
after it welcomes you and clears
the screen and everything--
here, let's just check
something like, if self.goal,
which is the original list of lists that
we created, if this is = to self.board,
return false.
So that we terminate the listener.
Otherwise, just return true.
OK.
Yes.
And here now, what we can do
now, is just return refresh.
Or if you'd return true to this
listener and it doesn't do anything--
so here, what we can also
do is just return b.refresh.
Wait.
What am I doing here?
Oh, if the game is over.
Here, return, b.refresh.
Actually, I don't think
I need to do this here.
I think this would be sufficient.
Right?
OK.
Let's try this out.
And now if I solve, OK, it's done.
So we might want to give it like
a, congrats you won, type of thing.
So here we'll say if the board--
is kind of like more intuitive for me to
ask the question the other way around.
I don't know why?
If the board is equivalent
to the goal, print, Congrats!
You won!
COLTON OGDEN: Nice.
RODRIGO DABOIN SANCHEZ: And maybe
like separate it a little bit
from the board.
Then we fire that message.
And here--
COLTON OGDEN: Then it will end.
Right?
RODRIGO DABOIN SANCHEZ: --we will say,
py.mean.py if we solve it, Congrats!
You won!
Game is over.
COLTON OGDEN: You can
test it manually too.
If you were like to it only
do like five, or maybe let's
three shuffles or whatever.
RODRIGO DABOIN SANCHEZ: Let's bring
down the shuffle magnitude to like five.
It's very likely that we'll have
to rerun it a few times in case
we automatically win.
COLTON OGDEN: Yeah.
RODRIGO DABOIN SANCHEZ: Yeah.
Like see, we just started and won.
COLTON OGDEN: See, it
doesn't quit there.
See that?
RODRIGO DABOIN SANCHEZ:
That's interesting.
COLTON OGDEN: So there's a slight bug.
RODRIGO DABOIN SANCHEZ:
But that time it did.
COLTON OGDEN: Oh, did you rerun it?
RODRIGO DABOIN SANCHEZ:
Yeah, I just did.
This time it didn't.
COLTON OGDEN: I'm not
sure why that's the case.
RODRIGO DABOIN SANCHEZ:
This time it didn't as well.
Interesting.
COLTON OGDEN: Looks
like most of the time--
are you sure you reran it?
RODRIGO DABOIN SANCHEZ: And look,
that time I reran it, and it did quit.
That's so strange.
COLTON OGDEN: Yeah, that is a bit weird.
It's doing it every single time too.
Like you're winning every time, almost.
RODRIGO DABOIN SANCHEZ:
Let's increase the magnitude.
COLTON OGDEN: Oh, are you hitting shift?
RODRIGO DABOIN SANCHEZ: I only--
no.
I was just--
Oh, was I?
I was hitting shift.
Was I?
No.
COLTON OGDEN: I don't know.
RODRIGO DABOIN SANCHEZ:
I don't remember.
All right.
The chat will tell us.
COLTON OGDEN: Now you can
try and solve it, right?
RODRIGO DABOIN SANCHEZ: It'll
be funny if I can't, right?
COLTON OGDEN: I mean, it's
not an easy game to do, right?
RODRIGO DABOIN SANCHEZ: Congrats!
You won!
COLTON OGDEN: OK.
So we know it works.
RODRIGO DABOIN SANCHEZ:
I'm pretty sure that
wasn't the optimal way to solve it.
COLTON OGDEN: Probably not.
RODRIGO DABOIN SANCHEZ: OK.
Good.
So our game over screen is working.
Right?
Now we will implement a
more robust solve function
that uses Breadth-first search.
So maybe people in the stream may not
be familiar with Breadth-first search.
Maybe what we need to
talk about that first.
Solves the game using
Breadth-first algorithm.
Let's just pull up a
tree and talk about it.
Binary tree.
Maybe it doesn't have
to binary-- search tree.
Search tree.
OK.
COLTON OGDEN: It's a little hard to
see in the chat, or in the stream.
RODRIGO DABOIN SANCHEZ: How's this?
COLTON OGDEN: Oh, that's good.
RODRIGO DABOIN SANCHEZ:
So this is just--
for those who are not familiar with
search algorithms, the purpose of them
is if you're trying--
in the context of maybe
like AI or something--
if you're trying to like,
for example, in our game,
let's say this is the
state of the board,
and we want like an artificial
intelligence to solve the game for us.
It's going to take this initial
kind of screenshot of the game
and then it's going to have to search
through different possibilities
that it could foresee if
it makes certain moves.
So obviously, you know, the AI would
have four possible moves to make here.
If you can move up,
left, down, or right.
Obviously, if it moves down to
right, it's not going to do anything.
But it won't know that until it does
it and sees the new state of the game.
If it moves up, we'll
have something like this.
And then this will be
like a new possibility
and from here it can move
up, left, right, down,
again, and so on and so forth.
And so in the context
of a search tree we
can model the game as a
tree, where the root, which
is the very first element in the
tree, is the initial starting
point of the game.
And then instead of having only
two directions here, maybe it
has four branches, one four
up, down, left and right.
And then if it makes a move
up, it's at this new state
of the game, where once again, it's
faced with, it can move up, down,
left, right.
And so you can envision this tree with
four branches each time, progressively
getting larger and larger.
And so the way that
Breadth-first search works--
so pretend that our goal state is
to 7, at some point down the line
it's going to get to a point
where the screenshot of the board
is actually 1, 2, 3, 4, 5,
6, 7, 8, 9, 10, 11 whatever.
Let's pretend it's the 7.
The way that Breadth-first search works
is first it starts, it checks the root.
Then if that's not the goal,
it checks, you know, this node.
Every kind of like circle in
this tree is called the node.
Then it check this node.
Then it will check this one.
And then if neither of
these are the goals,
it will move on to the
next level of the tree.
And check this, this, this,
until it checks this, and then
finally gets here.
That's opposed to another algorithm
called Depth-first search,
where it just goes all
the way down one branch.
So like, let's say the AI
says, I'm going to move up.
And it's going to keep, you know,
going down that like, path in the game
without even considering what
would have happened if it had
moved to the left as its first move.
Then once it reaches the conclusion,
which actually might be really, really
long if, you know, in the
original state of the game
all you have to do to win is move left
for some reason, or move up, or down.
But if you move in a
different direction,
you have to like do a bunch
more moves or whatever.
So Depth-first search could potentially
take like a really long time
if it goes down the wrong branch.
Whereas Breadth-first search
is just going to be like,
OK, I'm going to try moving up.
That wasn't the win.
Let me try moving left.
That wasn't the win,
and so on and so forth.
So we're essentially
going to just be kind
of traversing a tree level by level
as opposed to branch by branch.
COLTON OGDEN: To that note, Bhavik had
said, "Does Breadth-first search always
take fewer steps than
Depth-first search?"
RODRIGO DABOIN SANCHEZ: No.
Because what if the goal was--
COLTON OGDEN: Number one or whatever?
RODRIGO DABOIN SANCHEZ: Well,
in this example, it would be.
But we can draw, right?
I like going to this blank slate.
And then, it's for
typing, but I have some--
so like, oh, this is an
old thing that I had.
COLTON OGDEN: Wouldn't it funny
if there was like something
like really blasphemous?
RODRIGO DABOIN SANCHEZ: No.
I don't-- I don't condone
blasphemous activity.
Erase all.
OK.
COLTON OGDEN: You could
I was also draw.CS50.io.
RODRIGO DABOIN SANCHEZ: Oh, really?
COLTON OGDEN: Yeah.
Oh, but you have a touch screen too.
That's sick.
RODRIGO DABOIN SANCHEZ: Yeah.
COLTON OGDEN: I didn't know that
this was touchscreen tablet.
RODRIGO DABOIN SANCHEZ: So
if we can imagine kind of
like a tree where you
have a really long branch,
and then maybe like, a lot of
other branches per level, right?
And then these each had
like a lot of branches,
whatever, blah, blah, blah, blah.
Using Breadth-first
search, you would have
to search this one, then this one,
then this one, then this one, then
this one, then this one, and this
one, this one, and this one, this one,
this one, until you got here.
Whereas if you use
Depth-first search you
would just, whoop, automatically get
to the end and be like, oh, that's it.
But by the same token, if the goal
state is over here for some reason,
Death-first search would
look through this branch,
then it would look through this
one, then through this one,
and through this one, this
one, until it finally got here.
Whereas Breath-first search would
check this, this, this, this, and then
find it.
So depending on the--
COLTON OGDEN: Way the tree is set up.
RODRIGO DABOIN SANCHEZ: --on
the way that the tree set up.
One is faster than the other.
So I just arbitrarily
was like, OK, let's just
use Breath-first search to search.
OK.
COLTON OGDEN: BlueBooger had
mentioned that Red Blob Games
website has excellent tutorial on
Pathfinding and Python and game
programming.
I think I actually checked
out that once in a long time
ago when I was getting more into
like procedural generation algorithms
for like rougelikes and stuff.
They have, I think if
it's the same website,
they have a lot of really
cool resources on that.
So yeah, definitely check that out.
RODRIGO DABOIN SANCHEZ: All right.
So let's give it a crack.
Let's try to implement this.
Right?
Where's my editor?
Here it is.
COLTON OGDEN: Oh, Bokkengro, "You
guys should add a description
to your stream."
Yeah.
That's a good point.
We should.
Thank you for mentioning that.
I will I'll do that soon.
"Are you guys just two random
dudes doing Python tutorials,
or what is this?"
Yeah.
So this is-- so CS50
is a course at Harvard
that has a YouTube channel and
Facebook and all this stuff.
And we're kind of an extension of that.
We're based at Harvard,
and Harvard University.
And we have access to a bunch of people
here that all know a lot of stuff.
So, Rodrigo, is a CS
student here at Harvard.
And he whipped up this Game of
Fifteen problem set from a prior year,
and he's doing it in Python.
I do game programming.
We've got David J Malan, who hosts, who
sort of lectures CS50, and sort of mans
the ship, so to speak, does
a whole bunch of other stuff.
So, yeah, definitely check us out.
Watch some of our prior vlogs.
Go to our YouTube channel,
youtube.com/cs50 if you're curious
about our lectures and whatnot.
We do a lot of different stuff.
So we're just kind of new to twitch.
So growing our presence
on this platform.
RODRIGO DABOIN SANCHEZ: Also, I want
to touch on something that I just
saw someone write in the chat.
Where let's say we have an example--
so the comment was from Andre,
Breadth-first search is
exhaustive and it will certainly
find the shortest path.
So that's a good point.
Say we have, like especially
in the type of game,
where you have multiple ways
of arriving to the same state.
Right?
Like earlier-- do I
still have an example
here where I could have probably gone
left, up, and down, and you know,
won the game, but I went in the
other direction like a few more times
and finally won it.
So we have multiple ways of
getting to the same state.
Some of them will be
shorter than others.
And so let's pretend that we
have multiple goal states.
So maybe square is the goal state.
And so we can win the game either
by making one move, or making three.
So Depth-first search will find this
solution, which is potentially longer.
But Breadth-first search,
since it goes level by level,
it checks every node at each level.
So it will inevitably find
the shortest solution.
Whereas Depth-first search
might find another solution,
but it might be longer, for example.
And there are certain--
what's it called?
Possibilities, where one branch might
go on indefinitely and so Depth-first
search never checks any other
part of the tree or something.
Another note about
Breadth-first search is
it'll find the shortest path to victory.
OK.
Good note.
OK.
So what we're going
to do is since this is
going to be probably a
more lengthy function,
is we're going to map it out first.
We're going to write
what we're going to do.
So we want to have--
one thing that we want
to avoid is like getting
stuck in some sort of infinite
loop, where we make a say--
we want to remember
states we've been in.
Right?
We don't want to we don't want the AI
to start off at some starting state,
make a few moves, end up right
where it was at the beginning,
and then check the same moves
that he had checked before,
and then, you know, get stuck making
the same pattern over and over
and over again.
So we'll probably want, you
know, like a searched list,
where we can store configurations
that we've been in before.
We'll also want what is
known as a fringe, which
is essentially kind of
like a queue of like,
nodes that we are waiting to check out.
So like in the tree that I
drew out, if in the first level
there's four different nodes, so
like, going up, right, down, or left,
will want to just put those in the
queue for things that we can do.
And then check those,
you know, one at a time.
So we'll want to actually
make this a queue.
And we're going to need
to import from q that
is after, no that is before r, right?
COLTON OGDEN: Q is right before r.
RODRIGO DABOIN SANCHEZ: Yeah.
COLTON OGDEN: Q, r, s,
t, u, v, w, x, y, and z.
RODRIGO DABOIN SANCHEZ:
So we're going to import--
LOL.
We're going to import the queue
class from the queue library.
And we're going to have our fringe be q.
If you wanted to do that first
search I think you would use a stack,
because then it's last
in, first out, right?
But anyway, let's not go
down that rabbit hole.
So we're going to use the q
to do Breadth-first search.
And I wish I would stop erasing--
here we can use this tree.
If we use a q, the way
that we do this is,
we're going to check the current node.
We're going to say, is
this the goal state?
If it is we can just break out, return
the path that we took to get there.
If it's not, we want to
check its successors, right?
Or its children.
COLTON OGDEN: So every node then,
just a copy of the board, basically?
RODRIGO DABOIN SANCHEZ: Essentially.
It's like a copy of the
board, and we're also
going to want to store like the current
path that we've taken to get there.
So that at the very end, we can return
that path and then make those moves.
And that way we can
actually visually see
the program solving it step by step.
Instead of--
COLTON OGDEN: Wow, this could
get pretty large, couldn't it?
If you're adding a copy of the board
every node, every time we make a move.
RODRIGO DABOIN SANCHEZ:
It'll get large, which will--
COLTON OGDEN: What's the
mental memory-- have you
measured the memory cost for
like 1,000 shuffled boards?
RODRIGO DABOIN SANCHEZ: So I
was actually messing around
with that earlier, and
originally, before I did this
I had to be the shuffle
magnitude set to 10,000.
And so I was like, man, I am
pretty sure I wrote this correctly,
but like, it's returning anything.
And then I was like, I should
print, you know, all the nodes.
And so I just see, for like
minutes, it's scrolling.
Like all this different
stuff it's thinking.
So I lowered the magnitude
to be like five moves
and then it just like
solved it pretty quickly.
COLTON OGDEN: I wonder if
you can like discard prior--
like set prior nodes
of the tree to nothing
because you're caching the path.
And have that still work.
Would that work?
RODRIGO DABOIN SANCHEZ: Yeah.
So that's what we want to
have this searched list for,
that I have initialized here.
So we can keep track of
things that we've seen before.
Such that if we see them, we
don't look at that successors.
We just stop looking at
that branch altogether.
COLTON OGDEN: Right.
RODRIGO DABOIN SANCHEZ:
And that should help.
OK.
So we want to keep track of states
of the game that we've seen already.
We want to have a queue of like
nodes that we're going to check out.
Like moves that we can make
from the current position.
And then we want to have
a root for the tree.
So like the initial
starting point is going
to be whatever the current board is.
COLTON OGDEN: Yeah.
RODRIGO DABOIN SANCHEZ: OK.
So now what we want to do is add--
COLTON OGDEN: How, what we-- are we
using the queue to represent the tree?
That's just the current
level of the tree, right?
The searched for the fringe?
RODRIGO DABOIN SANCHEZ: The
queue will hold all of the--
so like in this example,
what we'll do first,
is we'll add the root to
the tree-- to the queue.
And then we'll pop it from the queue.
Check whether it's the goal node.
If it is, we'll return.
If it's not, we will say OK, if
we haven't seen the state before,
go ahead and add to the list
of states that we've seen.
And then after we do that,
we will find its successor.
So we'll see, where can we go from here?
Then we'll store all of its
successors in the queue.
And then for, you know, kind of
repetitively for each new successor
we'll be like, OK,
we'll pop the first one.
queue FIFO, q is first in, first out.
So this one would be like, OK, we'll
pop this from the queue and we'll say,
is this the goal state?
And if it's not, then we will
you know check its successors,
add them to the queue.
But then since it's a
queue, we'll move on
to the one that was
added after this one.
COLTON OGDEN: I see.
So it'll never-- so what's hinging on
making this work is we will never, ever
see the same state again
in what we're solving.
RODRIGO DABOIN SANCHEZ: Right.
COLTON OGDEN: Like, otherwise,
this would be impossible.
RODRIGO DABOIN SANCHEZ: Yeah.
COLTON OGDEN: OK.
That makes sense.
RODRIGO DABOIN SANCHEZ: We will use
this list to keep track of things
that we've seen.
So we don't get stuck infinitely
making the same pattern of moves,
never making it happen.
COLTON OGDEN: I wonder how much--
so that I could see
taking all our memory too.
Like how much memory
do you think it takes
to store every possible
permutation of the board?
How many-- first of all, how many
possible permutations of the board
are there?
RODRIGO DABOIN SANCHEZ:
So let's see, we have 16--
is it like 16 factorial?
COLTON OGDEN: I don't know what--
I don't know what it would be.
RODRIGO DABOIN SANCHEZ:
Because it could be--
it could be you know--
COLTON OGDEN: Because I don't know
if you can, strictly speaking, get--
you can't get every possible
numerical permutation on here
because it has to follow a pattern.
Like the invariant has
to stay consistent.
RODRIGO DABOIN SANCHEZ: True.
Yeah.
So less than 16 factorial.
COLTON OGDEN: Yeah.
It would be less than that.
But I don't know what the
number actually would be.
But it's interesting.
RODRIGO DABOIN SANCHEZ: I'm sure we
can-- it's a quick Google search.
How many possible configurations
of Game of Fifteen?
COLTON OGDEN: Or Fifteen Puzzle.
It might be known by Fifteen
Puzzle, more frequently.
RODRIGO DABOIN SANCHEZ:
Ah, I can't type.
How many different possible
combinations are in the Fifteen Puzzle?
COLTON OGDEN: [INAUDIBLE].
RODRIGO DABOIN SANCHEZ: 10 to the 13.
COLTON OGDEN: 10 to the 13.
So, OK.
So there's 10 to the 13 board positions.
Those are all the strings that
are two bytes each, right?
So this is interesting.
Now, what I have the instinct to do is
to hash all the potential positions,
right?
To take that data structure, that
list data structure, and hash it.
And then store the
hashes in a dictionary.
And that way you can immediately
check to see whether that hash exists
in the dictionary.
Right?
Because how are you checking
prior positions now?
Are you just going over every single
one of them and comparing them?
Is it with equals?
equals, equals.
RODRIGO DABOIN SANCHEZ: I'm saying,
like if current board not in search
of lists.
COLTON OGDEN: OK, so you are
doing equals, equals effectively.
OK.
That's what that essentially--
RODRIGO DABOIN SANCHEZ: That's probably
why it takes so long for larger.
COLTON OGDEN: Yeah.
RODRIGO DABOIN SANCHEZ: As
it gets longer and longer.
COLTON OGDEN: So what
you can do is you can
hash-- if you hash those lists, right?
That data structure, find a way
to hash it, and deterministically
get it to hash.
You could store them in a dictionary and
then do constant-time look up of that.
RODRIGO DABOIN SANCHEZ: Dictionaries
are constant-time right?
COLTON OGDEN: Well, are
roughly constant-time.
Yeah.
So that would almost be very fast.
RODRIGO DABOIN SANCHEZ:
And actually, if we--
what's it called?
If we used to set for the searched
instead of a list, that would be--
searched is constant-time
look up in Python, I believe.
COLTON OGDEN: In a set?
Yeah.
Yeah, it is.
Because they hash it underneath the--
RODRIGO DABOIN SANCHEZ: So
we could use a set instead
of a list, which I have to--
COLTON OGDEN: What if--
can you-- I don't know if you
can-- well, I guess you can hash.
That's what I'm saying,
the tricky part would
be finding a way to hash [INAUDIBLE]
like that deterministically.
That'll be the tricky part.
RODRIGO DABOIN SANCHEZ: You know
something we could do is we could--
COLTON OGDEN: You could turn it into
a string and then hash that string.
You could-- by-- if you--
because the board is always going
to be a different string, right?
You could literally just
make a string of the board
as it is laid out with the brackets.
RODRIGO DABOIN SANCHEZ:
Like 1, 2, 3, 4--
COLTON OGDEN: Yeah, with the brackets.
Because then you would ambiguously
have 11, and stuff like that.
RODRIGO DABOIN SANCHEZ: True.
True.
COLTON OGDEN: So if you
do that, if you flatten
the ne-- flatten your list into
a string with the brackets,
you can hash those strings.
Those you can then store in a set, or
you can store them in a dictionary.
But sets would work fine as well.
And then you get almost
constant-time look up for those.
RODRIGO DABOIN SANCHEZ:
And so the only thing
that we're going to be
changing this algorithm
is how we're representing
the search list, right?
COLTON OGDEN: Yeah.
The search is no longer
list, it's going to be a set.
And every time you get a-- you
are looking at a brand new--
set is not indexed.
Be aware of that.
RODRIGO DABOIN SANCHEZ: That's fine.
We won't need the check indexes.
COLTON OGDEN: What was I going to do?
Oh, I was going to check
to see what 10 to 13 was.
10 to the 13 is equal to--
RODRIGO DABOIN SANCHEZ:
More than a trillion.
COLTON OGDEN: That's a big number.
Yeah, it's like-- what is that?
A thousand, million,
billion, 10 trillion.
RODRIGO DABOIN SANCHEZ: 10 trillion.
COLTON OGDEN: So 10, you could have
10 trillion different nested lists--
RODRIGO DABOIN SANCHEZ:
Here's what I'm thinking.
COLTON OGDEN: --that you're doing
an if statement on in your thing.
RODRIGO DABOIN SANCHEZ: Here's
what I think would be fun.
Here's what--
COLTON OGDEN: Well, I mean--
And that's-- is that potential--
that's just for the sake of the Fifteen
Puzzle.
Right?
RODRIGO DABOIN SANCHEZ: Yeah.
COLTON OGDEN: That's insane.
RODRIGO DABOIN SANCHEZ: Here's
what I think would be fun.
Let's implement that using
a list, see like at what
point it starts getting really long,
then change the search list to be--
COLTON OGDEN: Oh, sure.
We could time it.
Yeah.
RODRIGO DABOIN SANCHEZ: Yeah.
And then we could just--
COLTON OGDEN: That would be sick.
Let's do that.
RODRIGO DABOIN SANCHEZ: Because
we'll have like a working version.
And then, you know,
once that's done, we can
mess around and see like
how much of an impact
we can make just by changing the search.
COLTON OGDEN: Yeah.
Yeah.
Yeah.
RODRIGO DABOIN SANCHEZ: OK.
That sounds fun.
COLTON OGDEN: That does sound fun.
RODRIGO DABOIN SANCHEZ: OK.
OK.
So like we were saying,
we want to have some data
structure in which we keep track of
all the states that we've searched.
We want a fringe to be keeping
track of all the different moves
that we want to try.
And we want to kick off
the tree with a root.
So the root is going to
be the current board.
And now what we want to
do is we want to add our,
you know, our root to
our kind of like tree.
So the way that I wanted to do
this was making a dictionary, where
we're going to have a couple things.
We're going to have the board
that we're keeping track of,
and that's going to be the root.
Then we're going to keep track
of the empty spots location.
And that's going to be the
current empty spot location.
And we also want to
keep track of the path
that we've taken to
reach the current state.
So at the root, we're going
to have the current board--
I guess I don't need to write this out
as it's own separate variable, right?
OK.
So originally, so now this is the root.
Because the node isn't just
going to be the board itself.
It's going to be kind of like a
struct containing some information.
We're going to store the current board,
the current location of the empty spot,
and then the path that we've
taken to reach this node.
The root node, we've taken no steps
to reach it so the path is just empty.
And here it doesn't matter
whether we use a list
because we're only going to be adding
like some small number of moves
to get--
we're not going to do like more than
probably a couple of hundred moves
to solve it, I think.
I'm sure this is solvable
in a few number of moves.
OK.
So now we're going to kick off an
indefinite loop using, while true.
This is where we're actually
going to start searching the tree.
So the first thing we want
is kind of like a base case
that we just want to quit if--
and let's just write this out
so we know what we're doing.
Quit if no solution is found.
Which we should never
have to worry about,
but in case, this is important
to write when you're writing it
for the first time, in case you
accidentally introduce a bug
into your code, and then
your algorithm doesn't work,
this way at least you break out of
the loop once the queue is empty.
So if fringe.empty-- and the fringe is
just a native Python q and so instead
of like nq, dq, and whatever, what
have you the methods are put empty
and I think, get.
So this checks whether
the fringe is empty.
And here we're just going to
return an empty path like nothing.
OK.
So that's kind of our base case.
So now what we're going to do is
we're going to dq to the current node
from the fringe.
So node is going to be fringe.get.
Get is the q function that will return
the current first element in that q,
And we'll put it in this node variable.
All right.
And so let's write out the
algorithm-- inspect current node.
All right.
Now we want to check, is the
current node the goal state?
So quit if node contains goal.
All right?
Here we'll check if node at the
board position is self.goal.
We'll return the path
that node contains.
So ultimately, what we
want the solve function
to do for us is to return like the
list of steps that we need to take.
The list of moves that we need to
make in order to solve the game.
So if we find that the board in the
current node matches the goal board,
we're just going to return the path
that is attributed to that board.
COLTON OGDEN: Hey!
How's it going?
RODRIGO DABOIN SANCHEZ: And we
have, David, joining on the stream.
COLTON OGDEN: The mystery guest.
DAVID: Nice to see everyone.
COLTON OGDEN: So we're doing
a Game of Fifteen in Python.
Currently we're on the solver right now.
So where we talked about it earlier--
we're doing a sort of like a Breadth
DAVID: Breadth.
RODRIGO DABOIN SANCHEZ:
Breadth-first search.
COLTON OGDEN: Breadth-first search.
DAVID: Still hung up on that, huh?
Why don't we do a Depth-first search.
RODRIGO DABOIN SANCHEZ:
Well, don't worry.
Earlier I was asking Colton
what order the alphabet is in.
So there's that.
DAVID: Oh, did you go with alphabetical?
RODRIGO DABOIN SANCHEZ:
I tried to, yeah.
DAVID: Nice to see everyone though.
I look forward to seeing this.
COLTON OGDEN: We're doing
an interesting experiment.
So right now we have basically a--
this whole algorithm hinges on
checking prior states of the board
because the board can only
ever be in one state at a time
while we're solving it.
And basically, we can,
instead of, assembling
this massive list of
prior states and checking
to see if we've already seen that state.
We can hash the state of the board
as a string and store that in a set
and then do constant-time
look up of that hash string.
And see how that comparison
actually ends up [INAUDIBLE] time,
how much time we save.
RODRIGO DABOIN SANCHEZ: But
first, we're restoring everything
in the list to see like what
the difference in time is.
If we start off with the
list, versus something
that's going to have confidence--
COLTON OGDEN: Because this
list is going to be massive.
Especially if we've done 1,000 steps
to get to the permutated board.
DAVID: But you're just
using these hashes
to determine if it's
in a one configuration?
COLTON OGDEN: If we've encountered
this state before while we're
going through the actual algorithm of--
DAVID: Oh, at which point, you'd--
just so you're analyzing
the answers that--
COLTON OGDEN: We are.
Exactly.
Yeah.
DAVID: Oh, OK.
OK that's, good.
I a little nervous there.
Because if you pre compute all these
hashes, that's like infactorial.
COLTON OGDEN: Oh yeah.
Yeah.
No.
It's like 10 trillion different ones.
No.
No we would, no.
We would not do that.
Actually, we just looked it up
on the calculator right there
to figure out the exact number.
DAVID: Oh, my god.
You have to think about where
to put the commas there.
COLTON OGDEN: Yeah.
Nice.
DAVID: All right.
Well, let me leave you to it.
But, good to see everyone.
I'll hop on the chat and looking
forward to seeing this develop.
COLTON OGDEN: Cool.
RODRIGO DABOIN SANCHEZ: Sweet.
COLTON OGDEN: Thanks for hopping over.
RODRIGO DABOIN SANCHEZ: All
righty, so, so far we, OK, we
have dq'd the root from the q.
And we are inspecting
whether it is the goal state.
If it is, we are returning
the path that it contains.
Otherwise, if it's not, we want to
add the CurrentNode to our search
set, which right now is a list.
And then put its children in the fringe.
So if the root node
isn't the goal, we want
to add it to the list of
search states that we've seen.
And then we want to put
its children in the fringe
so that we can repeat this
process again with them.
COLTON OGDEN: Right.
RODRIGO DABOIN SANCHEZ:
So the way we're going
to do that is we're going to check,
if node board, not in search,
which is what we're
going to change later.
COLTON OGDEN: I mean, we can still--
it's still going to be not in search,
right?
Because you're doing it through--
you're going to look in a set, though.
RODRIGO DABOIN SANCHEZ: Yeah.
Yeah.
We're going to change
what search looks like.
Which will dramatically--
COLTON OGDEN: This will actually
be pretty much identical.
RODRIGO DABOIN SANCHEZ: Which is nice.
Which is why something as simple as
what data structure sure you're using
can completely change the runtime
of your code, or the efficiency.
COLTON OGDEN: Oh, yeah.
100%.
RODRIGO DABOIN SANCHEZ: OK.
So we're going to go ahead
and append to the search list
the current board that we're seeing.
COLTON OGDEN: Also, it's funny that
Asli was lurking this whole time.
So glad to know that you've
been in the chat, Asli.
Everyone is saying hi to David.
If you use Dijkstra's shortest path
alogorthm on a graph with all the edges
it just becomes breadth first search.
Yeah.
I don't know too much about it.
I have to reread up on Dijkstra.
It's been a long time since I
looked at any of that stuff.
RODRIGO DABOIN SANCHEZ: All right.
So now we're going to want some
function to extract the successors
from our current node, right?
So let's just pretend that we
have a successors function.
COLTON OGDEN: Are all
the successors going
to be up, down, left,
right, from that one node?
RODRIGO DABOIN SANCHEZ: Right.
Yeah.
OK.
So for child and successor,
which we have to write.
Successor doesn't exist right now.
We're going to say, if the child--
I don't know, now we're
getting into territory
where we haven't determined
the behavior of successors yet.
So actually, let's just
go ahead and define it.
So that, you know.
For child and successors, we want to
check whether one of those children
is already in the search list
so we don't add it to the queue.
Otherwise, if the successors of the
current node isn't in our search set,
we will add them to the queue.
Because say for example, that we're
starting at the lower right corner.
And this is the result
of moving to the right.
That would be exactly
the same as this one.
So we don't want to even consider this.
And so that will save us the time
of putting this in the queue.
So we will skip this and go to that
next one, which maybe like go up
or something.
So let's define-- let's
define a successor's, maybe
like a helper function within here
itself, within the solve function.
[CHIME ALERT SOUND]
COLTON OGDEN: Very
interesting to understand
most of things those guys are saying.
No, it's OK.
I mean, we've gotten into a little
bit of algorithmic territory
and I'm not even that good at
algorithms and data structures.
I've learned a few things
over the years and I
should study upon it a
little bit more detail,
but this is all good stuff
to kind of be aware of.
Just like using the right data, take
that away from the stream if anything.
Use the right data for the job.
Try to look for it, you know, when
you're manipulating data like this
and doing thousands upon
thousands of potential iterations
and all of those things take up data,
that's an opportunity to optimize.
And, Asli, I totally understand.
I have off days, as well, all the time.
But thanks for tuning in on the lesson.
Being around and still
absorbing the material.
That's really like a huge thing.
And that's something I
think that's important,
especially in like language learning.
It's just absorbing sort of passively,
you know, passive media ingestion.
Or at least, in this
context, like CS, vernacular
and just kind of getting
input, you know, input sources.
It's super important.
RODRIGO DABOIN SANCHEZ: All right.
So for the successor's
function, we know that we're
going to want four separate boards.
Because one of them
we're going to move up,
one of them we're going to move left,
one of them we're going to move down,
one of them we're going to move right.
So maybe not like the
nicest way possible.
I've made four copies of a board
and I've stored them in a list.
Same thing for--
COLTON OGDEN: How large is this
queue going to be at max, by the way?
Because we're popping off the queue
every time we check something right ?
Or are we just adding to the queue
and never getting rid of the--
RODRIGO DABOIN SANCHEZ: No.
We're popping-- every
time that we inspect
a node we're popping it from the cube.
COLTON OGDEN: OK.
So when might we expect 8.
Then we inspect 3.
We pop 8 and add 1 in 6,
after the 10, in this case?
RODRIGO DABOIN SANCHEZ: We do.
Pop 8.
We add 3 and 10 to the queue.
COLTON OGDEN: Then we go to 3.
We pop 3, add 1 and 6 to the
queue go to 10 and then do that.
OK.
I see.
Got it.
RODRIGO DABOIN SANCHEZ:
So at most we're only ever
having two, less than
two levels of the tree.
COLTON OGDEN: Yeah.
I mean it can get large later, but
it's not going to be like insane.
RODRIGO DABOIN SANCHEZ: Yeah.
Nice.
OK.
So blah, blah, blah. e_loc,
and e_loc, and e_loc.
So as I kind of referred
to earlier, deep copy,
helps for recursively copying the list.
But if you just if you know you're
working with a shallow list that's
only one level, you can make a copy just
by using the List function in Python.
That gives you a list.
And OK.
So now what we're going to do is we
want to say, like, for example, the--
and we can clean this up later.
But for now just--
COLTON OGDEN: I wonder, I wonder if,
even here, instead of deep copying,
like using the hashes.
Like the-- sorry, I
didn't mean to do that.
--using the string hashes.
Because all you're doing--
I mean, eventually we'll do
the new algorithm, right?
Like these-- what we're doing is we're
just comparing these, ultimately,
to what we've already
seen in the thing, right?
And comparing it to board, but
we can have a hash at the board
as well, right?
To solve the board, I mean.
RODRIGO DABOIN SANCHEZ: True.
Yeah.
Yeah.
The only reason-- the
only thing though is
that we actually need to make a move.
We need a-- well, this is the
current state of the board,
and so we want to see what
it looks like if we move it--
COLTON OGDEN: Oh, yeah, you're right.
RODRIGO DABOIN SANCHEZ:
So the only way we can
make a move is an actual list of lists.
COLTON OGDEN: Unless you wrote a
function that could take the hash
and do it on the hash.
RODRIGO DABOIN SANCHEZ: Yeah, that would
be a little bit more work, I think.
OK.
So here, what we want to
do, is say, self.move up.
And we're going to
have this be the same.
We're passing in the
board's move up function,
and we're passing in the location of
the empty spot to the move up function.
So we'll have this be here.
All right.
And let's clean this up
later, but for now, let's just
do this for the other ones.
Just because I want to have time
to mess around with the other ideas
that we've had.
And this one we'll be moving
right, this one moving down,
this one will be moving
left, and here we
have 0, 1, 2, 3, and 0, 1, 2, 3 yeah.
A loop might be better for this.
OK.
And so now, what we'll
want to return is--
this is going to be the children.
So we'll want to return potentially
a list of lists where here, we're
going to contain the board, position 0.
COLTON OGDEN: Bokkengro, thank
you very much for the follow.
I think I missed somebody else?
Apologies.
[INAUDIBLE],, thank you very
much for the follow as well.
Much appreciated.
RODRIGO DABOIN SANCHEZ: So I
didn't talk about how I want to--
how you want to--
what's it called I didn't
talk much about how
I want to represent the path list.
And the way that I want to do it is have
it be with numbers like we did before.
So 0 means up, 1 means right,
2 means down, and 3 means left.
So that's what I'm doing here. .
I am returning as the
kind of like children,
I am returning the current board, if
you move up or in whatever direction.
The look-- the current
location of the empty spot,
if you go in whatever direction.
And then what direction
you moved in as an integer.
So here I'm saying, board if you move
up, empty location if you move up,
and the fact that we moved up.
COLTON OGDEN: Right.
Because that's the information
that represents the path
we took to get to the actual thing.
Because we're going to
return that eventually.
And how are you returning that?
Is it just going to be a data
structure that just gives you
the list of directions in order?
Given the--
RODRIGO DABOIN SANCHEZ: Yeah.
It's just a list.
So then the path will look something
like, I don't know, 0, , 3 0,
up until it solved the thing.
And we'll see-- we're just
going to iterate over that list.
And kind of how we
shuffled the board, we're
just going to loop through all those
results and refer to this dictionary
up here.
COLTON OGDEN: And it would be cool
if you got the end result of that.
And then you have the starting
state of the board and then
like a function that like
performed every step every like 200
millisecond or whatever,
so you can watch it.
Oh, OK.
That's good.
That's cool.
RODRIGO DABOIN SANCHEZ: OK.
So now we know that successor's is going
to take in a board and the location.
So node at board and node at e_loc,
which I think is what we called it.
Yes.
And what is it?
Why did it change colors here?
I'm curious.
Did I close down this list?
Wait a minute.
This list here closes that.
And then this closes--
interesting.
I think I have some extra
brackets that I don't need.
OK.
So now this is closing that.
This is closing this.
This is closing that.
This is closing this.
And then this is closing that.
OK.
That looks better.
All right.
So now we know that the
node is going to take in--
that the successor sticks in a board
and the location, which in the node
we have a board key and a location key.
So now the child is going to be a
list containing a board, a location,
and then the direction.
So we want to check if we've
not seen the board in the child.
So if child is 0, not in searched,
we will add that child to the fringe.
So we will put, in the fringe,
a dictionary containing board.
And then have this be
child, deposition 0.
And then e_loc look is going
to be child at position 1.
And we're going to have path be--
here's what we've got to
remember the parents path.
So it's going to have to be the
path of the current node, plus child
that index 2 in list form so that
we can concatenate these lists.
So I'm doing essentially
something that looks
like, if I have x is a list containing
1, 2, and 3, x + 4 will give me 1, 2,
3, 4.
And so that's how I'm going to
update the paths throughout the tree.
So this might be like the current
node, and then the children
is going to be either 0, 1, 2, 3.
And so we're going to add it
as a list to the existing path.
And that's what is going on here.
All right.
So now I believe this should
return to us the path to victory.
So what we've got to do is
check in our main function,
where we have currently
right here, if we
press Shift where it's going to solve.
We want to store the path.
So we're going to potentially store
this in a variable called moves.
And if we want to be really fancy we
can print something like, thinking,
because we might take a few seconds.
OK.
So we're going to have
the path right here.
We know that we have to return
false when we want to stop--
when we want to quit the program.
So I'm going to preemptively have this
variable called persist, which is true,
because we we're going to want to
refresh the board with each move
that we make.
So we want to keep refreshing
such that we're returning true.
But once it's done, we
want to quit the program.
Actually, do we need to do this?
I don't think so.
OK.
Let's just do this.
Let's say for m in moves,
so for move, in the moves
that we need to make to win, we're
going to refer to the moves dictionary
that we have in the board, right here.
COLTON OGDEN: Shout out to Brenda,
by the way, for joining the stream.
A little late Brenda, you're all right.
But that's OK.
Like everyone is saying,
better late than never.
Glad to have you with us.
We're getting to the
really cool stuff here
in a second where we actually compare
running time of a sort of, I would say,
naive implementation, just caching
prior states of the board in string--
in lists and strings.
And then memoizing them
for when David walked in.
That was a much better word than
how I was trying to describe it.
RODRIGO DABOIN SANCHEZ: So after we make
a move, will want to refresh the board.
And then we actually have to
import from the time library.
Important sleep.
So that we can have the terminal
sleep for just a second,
so we can actually see
the move being made.
Right?
And then it'll finish doing this.
And it'll return refreshed.
OK.
Unless we missed something, let's--
oh, let's make sure that the
shuffle magnitude isn't too big.
It's just 10 right now.
OK.
Let's see what happens if we run main.
We automatically won.
COLTON OGDEN: Nice.
RODRIGO DABOIN SANCHEZ: OK.
So here it is shuffled a little bit.
Right?
And if we press Shift, it's thinking.
COLTON OGDEN: So this was-- so it's
not, in this case, showing you a like,
live figuring out the solution.
This is, it knows the solution
and then it's doing it.
It's actually doing the one
solution, that it found.
RODRIGO DABOIN SANCHEZ: Right.
We could also print the--
we could print the solution list, but I
mean, it would just look like numbers.
So the user wouldn't really
necessarily know what it means.
COLTON OGDEN: Right.
Yeah.
Yeah.
Yeah.
No, this is super cool.
RODRIGO DABOIN SANCHEZ:
But you'll see now
that if we increase the shuffle
magnitude to say, even I don't know,
20.
COLTON OGDEN: Just to
get a little bit lengthy.
I mean we can get rid
of the-- probably the--
RODRIGO DABOIN SANCHEZ: Oh, wow.
That was quick.
But I don't think it shuffled that much.
Some of the moves must have been--
actually, it looks
almost like the same--
the same moves.
That was--
COLTON OGDEN: That's pretty funny.
Yeah.
RODRIGO DABOIN SANCHEZ: All right.
COLTON OGDEN: This is
a little bit shuffled.
RODRIGO DABOIN SANCHEZ: Yeah.
So now like all of a sudden,
it's thinking a bit longer.
And we can start to play around
with changing the searched data
structure from the list to something
a little bit faster to search.
So something with constant-time
look up or near there.
Because I mean, already you're kind
of looking at this like, ah, man.
COLTON OGDEN: It's thinking.
It's thinking for a while.
RODRIGO DABOIN SANCHEZ: OK.
So while this thinks, do you want to--
COLTON OGDEN: Yeah.
Let's do the memoized
solution because that
sounds like it would be way faster.
RODRIGO DABOIN SANCHEZ: So here we are.
You we're recommending
using a set, right?
COLTON OGDEN: Yeah.
Set.
I mean, I originally
proposed dictionary,
but somebody wrote and recommended set,
and that actually makes more sense,
because all we're doing is seeing
that it's inside of a container.
Right?
And they all are unique just
per virtue of what they--
I mean, I guess it doesn't matter.
In the dictionary it
would be the same way.
But, set, sounds like it would be fine.
RODRIGO DABOIN SANCHEZ:
We have found sets.
COLTON OGDEN: But what we need to do
is we need to serialize the string,
or I don't know if
serializer is the right word,
but we need it to take the
representation of the list,
stringify it, and then use that as
the hash or the index into the set.
And we need to make sure we preserve the
brackets in that string, just because
the--
like you could have a situation
in which you have maybe like--
would this be the case necessarily?
Yeah.
Like 11, 12, versus like 11, 2, 3, 4.
Like it doesn't know if that's 1,
12, 3, 4, or 11, 2, 3, 4, you know?
So you do need to keep
the brackets in there
to preserve ambiguity in
certain situations, I think.
RODRIGO DABOIN SANCHEZ: Right.
COLTON OGDEN: So let's
definitely do that.
RODRIGO DABOIN SANCHEZ: I was
just re-familiarizing myself
with set notation.
COLTON OGDEN: Oh, yeah.
Yeah.
A lot of the data structures will
have kind of a uniform interface.
Just because like-- I forget
what they call it-- the Python,
like they have a term for it.
Like all the core like, dunder
functions they define that
let you have similar behavior
across data structures.
And you can like define them yourself.
RODRIGO DABOIN SANCHEZ: OK.
So let's say we have a
set based on this list.
If we want to add elements to it I think
it's s.add, like if we wanted to add 4,
and then we can say 4
in s, and that's true.
But 5 in s should be false.
But if we do s.add 5.
and then we do 5 in s.
And then s.empty,
potentially, is a function?
No.
I don't remember how to check.
COLTON OGDEN: You could
just say s is equal to set.
RODRIGO DABOIN SANCHEZ: No.
I'm checking, like checking, if--
COLTON OGDEN: Oh, if it is empty?
RODRIGO DABOIN SANCHEZ:
But I don't think
we actually do this in the algorithm.
COLTON OGDEN: No, I don't think we do.
No you're only doing
that with the queue.
Right?
RODRIGO DABOIN SANCHEZ: And I believe
you can have s2 just be an empty set.
Right?
COLTON OGDEN: Yeah.
RODRIGO DABOIN SANCHEZ: OK.
COLTON OGDEN: [INAUDIBLE] .
RODRIGO DABOIN SANCHEZ:
[INAUDIBLE] is empty.
COLTON OGDEN: You can
also construct with some--
there's another way to do it, I think.
RODRIGO DABOIN SANCHEZ:
OK so now we have
search does a set, which means
here, instead of search.append
we'll have search.add and we'll have
to change the representation of what
we're adding.
COLTON OGDEN: Yeah.
So node board is going to have to
be equal to the string or the--
yeah, just the string of the--
RODRIGO DABOIN SANCHEZ: I wonder--
COLTON OGDEN:
--representation of the board.
RODRIGO DABOIN SANCHEZ:
--if we have a list of lists
like 1, 2, and then 3, 4 here.
If we do str b.
That sufficient.
COLTON OGDEN: Yeah.
That seems pretty good.
RODRIGO DABOIN SANCHEZ: OK.
COLTON OGDEN: You're going to get some--
the extra bytes no matter actually.
Yeah.
RODRIGO DABOIN SANCHEZ: Let
me make sure we're not using--
COLTON OGDEN: Yeah.
So you could just construct-- you could
just do a string of node board there.
RODRIGO DABOIN SANCHEZ: Right here.
COLTON OGDEN: Yeah.
RODRIGO DABOIN SANCHEZ: Yeah.
str node board.
And that means the actual node
contains the board, which is good.
In case it needs to
move it up and stuff.
COLTON OGDEN: And I think you
need to do if stream node board
not it search there too, right?
Fuck, sorry, I keep doing that.
I just cursed on stream.
Sorry.
RODRIGO DABOIN SANCHEZ: That's OK.
COLTON OGDEN: Bleep.
Bleep.
Sorry.
That was inappropriate-- that
was, yeah, that was a slip.
RODRIGO DABOIN SANCHEZ: All right.
So if node board has a string.
It's not in searched.
And the same here.
If the string format of the child
is not in searched, let's see,
is there anything--
is there really anything
else we got to do here?
I don't think so.
Board-- we are--
yeah.
I don't know.
I think that's it.
Right?
We just changed them all the
strings and put them in a set.
COLTON OGDEN: I think so.
RODRIGO DABOIN SANCHEZ: So let's try--
COLTON OGDEN: I'm curious
how much to think--
how much-- what was the number
that you set it to before when
it had to think for a long time?
RODRIGO DABOIN SANCHEZ: The
one that we did on stream?
COLTON OGDEN: We just did, yeah.
RODRIGO DABOIN SANCHEZ:
I haven't changed it.
It is at 20.
COLTON OGDEN: It was 20 and
it was taking that long?
RODRIGO DABOIN SANCHEZ: Yeah.
Remember that the first
one was pretty quick,
but it's because I think the shuffle
was nicer, like closure to goal state.
COLTON OGDEN: Wow.
RODRIGO DABOIN SANCHEZ: So let's--
COLTON OGDEN: Is this
thinking right now?
RODRIGO DABOIN SANCHEZ: No.
I was like wait.
Did we already win?
But no, it's 9, 11, 12.
OK.
Wow.
OK.
COLTON OGDEN: That was fast.
Instantly.
OK.
So if we keep bumping it up.
RODRIGO DABOIN SANCHEZ:
Let's see if we do--
COLTON OGDEN: So to be clear,
we just took the algorithm,
the Breadth-first search algorithm,
and we changed it from being sort of I
guess not linear--
I guess linear search in
terms of looking at the data,
but we brought it and made
it into a set, I guess.
So this one's slow.
What was the number that
you used on this one?
RODRIGO DABOIN SANCHEZ: Here it's 40.
COLTON OGDEN: OK.
RODRIGO DABOIN SANCHEZ:
So it's thinking.
But I feel like it hasn't yet reached
the number of the amount of time
that it thought for the one that
was 20, when it was still a list.
COLTON OGDEN: Mmm.
Wait, what?
RODRIGO DABOIN SANCHEZ: And
if you look at this board,
it's actually decently shuffled.
Right?
5, 1, 7, 3--
COLTON OGDEN: Oh, yeah.
RODRIGO DABOIN SANCHEZ: 2, 10, 6, 4--
9, 14, 12, 8.
So it's not like-- for the other one--
COLTON OGDEN: It's finished now though.
RODRIGO DABOIN SANCHEZ:
It's done now-- yeah.
COLTON OGDEN: That was
still reasonably fast.
RODRIGO DABOIN SANCHEZ: And
that was 40 shuffled moves.
Right?
And so we can even just
see how long it takes
to solve it to kind
of get a sense for how
many moves it's been having to make.
COLTON OGDEN: So it'd
be cool to do like a--
that was even then many moves, but
I mean it had to go through the tree
and figure that out.
RODRIGO DABOIN SANCHEZ: But
Breadth-first search always finds
the shortest path to victory.
COLTON OGDEN: Right.
Yeah.
RODRIGO DABOIN SANCHEZ: So I wonder if
we kick this up even to, I don't know,
80?
COLTON OGDEN: Oh, man, that's
going to take a long time.
I feel like it'd be cool to do like
a time before and after, you know,
like to see.
RODRIGO DABOIN SANCHEZ: True.
COLTON OGDEN: Because it
feels like, I mean, 40,
it solved it in a fraction
of the time it took--
RODRIGO DABOIN SANCHEZ:
To solve it for 20.
COLTON OGDEN: --to solve it for 20.
Yeah.
So I'm not sure what it would be, like,
how much time we've actually saved.
But it's clear that we've saved a lot.
RODRIGO DABOIN SANCHEZ: Yeah.
We've definitely saved some time.
I mean, I guess for comparison, if
this, you know, doesn't take forever,
we can put it back to a list and see
that it basically never finishes.
Right?
COLTON OGDEN: Yeah.
You could do like a 20 one.
Spartangtr, thank you
very much for following.
RODRIGO DABOIN SANCHEZ:
What have the people
in the chat have been saying
while we've been doing this?
COLTON OGDEN: There's some
conversations going on amongst people.
I haven't been following--
RODRIGO DABOIN SANCHEZ: Because this
is a perfect time to talk to the chat
now, while this is thinking.
COLTON OGDEN: Yeah.
True.
True.
True.
So we shouted out Brenda
for coming into the chat.
Talking about Coursera courses.
And then some folks left.
Oh, Bhavik was saying, print the number
of the steps in the length of the path.
So we could just--
we can see actually, how
many times it's doing it.
Right?
RODRIGO DABOIN SANCHEZ: True.
COLTON OGDEN: Because that will help
us get a better idea of just how
long the algorithm is too.
RODRIGO DABOIN SANCHEZ: So clearly, even
with making this a string in the set,
this is--
80 shuffled moves is still--
COLTON OGDEN: It's still a long time.
RODRIGO DABOIN SANCHEZ: Yeah.
COLTON OGDEN: Yeah.
I'm sure we're saving a tremendous
amount of memory though.
RODRIGO DABOIN SANCHEZ: For sure.
COLTON OGDEN: Because
like, if you were thinking
about how much memory you needed to
store to write those lists of lists.
RODRIGO DABOIN SANCHEZ: Yeah.
COLTON OGDEN: That was probably a lot.
RODRIGO DABOIN SANCHEZ: A
string would take less for sure.
Yeah.
Yeah.
Yeah.
Yeah.
COLTON OGDEN: Well, a hash string.
It's not even a string that's being
stored, it's a hash of a string.
Yeah.
0 of 1 versus 0 of n, clearly.
Yeah.
Not quite 0 of 1, but the actual--
that's the look up of the hashed string,
but the actual Breadth-first search
is still going to take a long time.
RODRIGO DABOIN SANCHEZ: Yeah.
This might be a little
bit too much for it.
COLTON OGDEN: Virani is saying,
"This is just some magic."
RODRIGO DABOIN SANCHEZ: It is cool
though, to like, just sit back and then
watch it figure itself out right.
Yeah?
But yeah.
COLTON OGDEN: It be cool if
you could have a counter here.
You could keep refre--
I mean, I guess I would slow the
application down a little bit.
RODRIGO DABOIN SANCHEZ: Yeah.
Like anything where it has to do with
printing while you're in the loop,
it's going to really slow it down.
That's why I waited to do the searching
first, so it's as quick as possible.
And then do the refreshing the
screen after it's figured it out.
COLTON OGDEN: That make sense.
RODRIGO DABOIN SANCHEZ:
Because I mean we could.
We could have an updated counter.
But then that would just slow it down.
COLTON OGDEN: That would skew
the performance a little bit.
Yeah.
Mmm.
RODRIGO DABOIN SANCHEZ:
But I mean, other
than that, that's the Game of
Fifteen that I had ready to present.
COLTON OGDEN: Yeah.
No, it's great.
It was cool.
It was cool that we got
a chance to actually go
into like algorithms and data structures
a little bit, make it a little bit more
theoretical, and actually
apply it to something.
I got to stretch it a little bit.
But yeah, no, it was awesome.
RODRIGO DABOIN SANCHEZ:
We can probably even see--
COLTON OGDEN: Oh, yeah,
and they're saying
if you can push it to GitHub
they want to actually get
a chance to tinker with it.
RODRIGO DABOIN SANCHEZ: Oh, yeah.
I actually already have
in my GitHub, the--
let's go on my profile.
My GitHub is coderigo17.
COLTON OGDEN: Good name.
RODRIGO DABOIN SANCHEZ: I
already have the Game of Fifteen
here as one of my repositories.
COLTON OGDEN: What's the link?
I'll type it in the chat.
So that's GitHub.com And this
is without the change though.
Do you want to commit, or maybe
do a branch that has the memoized?
I mean, I guess it's easy enough
to make the change on the string.
Yeah, you could just do that.
RODRIGO DABOIN SANCHEZ: I mean, yeah.
All I had to do was put
str in the two places?
No, the three places.
Oh, and we changed it to a set.
So it's not too bad.
COLTON OGDEN: Yeah.
That's not bad at all.
That's something that if the
viewers are going to tinker
with the data structures based--
RODRIGO DABOIN SANCHEZ: If you
follow that does it get to it?
COLTON OGDEN: It should, yeah.
Let me check and make sure.
RODRIGO DABOIN SANCHEZ: Yeah.
There it is.
COLTON OGDEN: Is a public?
RODRIGO DABOIN SANCHEZ: Yeah.
Everything that I do is public.
COLTON OGDEN: Cool.
Cool.
RODRIGO DABOIN SANCHEZ: Yeah.
COLTON OGDEN: Everything except
the private things that you do.
RODRIGO DABOIN SANCHEZ: Well,
the only things that are private
are things that I don't own.
COLTON OGDEN: It's feel thinking, geez.
RODRIGO DABOIN SANCHEZ: I don't know.
COLTON OGDEN: I mean, even
if it does finish, we wont
know how many moves
it took, necessarily,
without watching the whole thing.
So yeah, that's unfortunate.
RODRIGO DABOIN SANCHEZ:
But if it does finish,
the fact that the stream is
recorded, and then people
can go back and watch it, means
that if you're really curious,
you can skip to whenever
this finishes thinking.
And then actually count.
But yeah.
I don't know.
COLTON OGDEN: That was awesome.
RODRIGO DABOIN SANCHEZ: Is there
anything else that people in the chat
want to talk about
while we wait this out?
Or if this takes too long,
we may just have to--
COLTON OGDEN: Yeah.
Well, we might cut it short here, you
know, if we don't have any questions
and it takes a bit longer.
But that was awesome.
Thanks for coming onto the
stream and putting this together.
You know--
RODRIGO DABOIN SANCHEZ: It was fun.
COLTON OGDEN: It was cool.
It was fun for me to participate in.
I don't do a lot of algorithmic
programming, at least
more on academic side.
So this felt like an academic
exercise, which was cool.
And I liked the fact that we kind of
took a jump and dived a little bit more
into analyzing the
performance of the application
and figuring out ways to do that
using just simple data structures.
Not even really going out-- not
even changing the algorithm,
just changing the fact
that we were using a set--
RODRIGO DABOIN SANCHEZ: Right.
COLTON OGDEN: --instead of
a list of lists of lists.
Right?
RODRIGO DABOIN SANCHEZ:
It would have been
interesting to see how
much longer it takes
using Depth-first search, or something.
COLTON OGDEN: Yeah.
RODRIGO DABOIN SANCHEZ: But,
you know, one thing at a time.
I mean, I'm hoping to be back on the
stream at some point in the future.
COLTON OGDEN: Yeah.
RODRIGO DABOIN SANCHEZ: Classes
are about to start next week.
So I don't know.
I don't know what that
will look like for me.
But you know, if I end up doing
something for one of my classes
that I think is cool, I can kind
of take inspiration from that
and do something in a similar spirit.
COLTON OGDEN: Yeah.
Let me know.
And then we can get you back up here.
And I know, that you're
always game for, if we
play some Super Nintendo or something.
RODRIGO DABOIN SANCHEZ: Yeah.
If there's games.
COLTON OGDEN: [INAUDIBLE] or NES because
you seem to be pretty into those.
You were there for both of those.
MKloppenburg saying, maybe an
algorithms stream or something.
I mean, it would tie-in
to this a little bit.
RODRIGO DABOIN SANCHEZ: Yeah.
It would be.
We could talk more
about different types.
I'm actually about to
take algorithms classes.
Because that's what I'm mostly
interested in is an algorithms.
I do enjoy coding.
But the part of coding that I enjoy
isn't like, looking up to syntax,
and getting the syntax right.
Right?
It's the actual problem solving portion.
COLTON OGDEN: Yeah.
That's the most important thing.
I mean there's tremendous--
I wish this actually was more of
a dramatic change in performance.
I mean, it might be, ultimately.
It might be a dramatic change in
performance, relatively speaking,
and we just-- it's because
it's just so much that we
have been able to see it through.
We have to time it and we'd have to see.
Like the 10 versus the 20.
RODRIGO DABOIN SANCHEZ: If someone in
the chat is, you know, very into this,
they could time this using the
list version, which is in the repo.
And then maybe-- it won't
be much trouble for me
to add an additional push, having
it be set and on the string,
and then compare the two afterwards.
And let us know next time or something.
Yeah.
COLTON OGDEN: That would be awesome.
MKloppenburg is saying, your
water looks like it's boiling.
Green screen magic?
Maybe.
RODRIGO DABOIN SANCHEZ: I only like
drinking 212 degree Fahrenheit water.
COLTON OGDEN: Yeah.
Boiling water.
Bella_Kirs, our pleasure
Bella, thanks for tuning in.
And to MKloppenburg and thank
you Bhavik for the kind words.
Appreciate it.
Virani, thank you.
Yeah, a few minutes, says Bhavik
It's taking a little bit of time.
Tomorrow we're going to
be taking a look at Ren--
yeah.
is it Render 50, which is a PDF
tool, or take source code files,
and a lot of text editors don't really
let you print, or expert, to PDF
from your text editor.
But what we needed was a way,
I mean David, especially,
needed this for lecture, to
be able to print source code
and be able to look at
it while giving lecture,
have it nicely formatted on the page,
and a few other different cool things.
And so just using Python, previously the
tool was written in PHP but we took it,
we made it in Python,
and completely redid it.
And so we'll have a little
bit of look at that tomorrow.
So we're getting more into the
Python stuff, which is cool.
Today was Python.
Veronica will be on stream in the
near future with Python as well.
And I'm sure we'll have
other folks that are
on stream for Python related content.
RODRIGO DABOIN SANCHEZ: And
the stream tomorrow is at 3:00.
Right?
COLTON OGDEN: Oh, yeah.
The stream tomorrow is at 3 PM.
It's not at 1:00.
So 3:00 PM Eastern Standard time.
I'll push an event notifi-- it's already
on an event on the Facebook page,
but I'll post another
post to kind of pub it.
And then it'll go on
Reddit, Twitter, et cetera.
You'll see it.
Tune in.
It's going to be good time.
David is going to be hosting that one.
David J Malan.
Yeah.
Going to be an excellent,
excellent time.
RODRIGO DABOIN SANCHEZ: This would
be a dramatic time for it to finish.
But I don't know if it will.
COLTON OGDEN: Yeah.
I don't know if it will either.
It's going to be a long time.
RODRIGO DABOIN SANCHEZ: It'll be
like the end of Inception, you know,
where the coin is spinning,
or the totem is spinning
and you don't know if
it's going to stop.
And they end it.
COLTON OGDEN: Well, it looks like
we have a little bit of content
that we can keep going off of.
So Bhavik says, you Ubuntu looks dope.
Oh, MKloppenburg, you're right.
You're right.
I don't think we officially
ever confirmed the time at 3:00,
and so I kept it as a draft.
But it'll go up tonight for sure.
And then the--
I guess when I make it
an event, I'll post it
to the Facebook group
and all that stuff.
And then I'll also start creating
events for next week streams.
We have a few lined up
for next week as well.
That's going to be a good time.
Kareem, next week's going to Flask.
I'm going to CSS next week.
Dave and I are going to
do code reviews next week.
Minesweeper-- it's going to
be a good week next week.
Yeah.
It'll be a good time.
I was going to look at-- what was this?
Bhavik was saying, you're
a Ubuntu who looks dope.
What desktop are you using?
RODRIGO DABOIN SANCHEZ:
As in the Ubuntu version?
COLTON OGDEN: Yes.
I guess the Ubuntu, and if you have a
graphical window manager or whatnot.
Like what you're using for that?
RODRIGO DABOIN SANCHEZ:
I'm on 18.10 Ubuntu.
And I'm actually, ironically enough,
I'm not like a computer person.
Right?
So like--
COLTON OGDEN: That's OK.
A lot of people have this, I
think, stereotyped perception
that all CS people are also
like computer technicians.
RODRIGO DABOIN SANCHEZ: It's definitely
something that I want to improve on.
COLTON OGDEN: Yeah.
I think it's definitely valuable.
I mean, like, computer
parts aren't really
hard at all to understand, or to use.
But I mean, like
troubleshooting hardware issues,
I think that's something that takes
a lot of experience and training
and stuff, and so CS people usually
do are you familiar with the software
side, the hardware side
is not really something
they necessarily have to interact with.
RODRIGO DABOIN SANCHEZ: But I do know
that it's 18.10 and I had to configure
my bash rc to have it look the way--
that my terminal look the way it does.
But other than that, yeah,
I'm not too knowledgeable.
But--
COLTON OGDEN: That's all right.
No one is going to hold
you accountable for that.
I don't think.
This is an awesome stream.
Appreciate it again.
Like part of me wants
to keep trying to--
RODRIGO DABOIN SANCHEZ: I know.
COLTON OGDEN: --prolong it just
so we can see if it finishes.
RODRIGO DABOIN SANCHEZ: It would be
funny if as soon as we cut the stream
it ends.
Right?
COLTON OGDEN: Yeah.
That would be pretty funny.
Yeah.
Asli, our pleasure.
Ronnie was excited to see it finished.
Ronnie you should take
it and run it yourself.
Clone off of GitHub and try it out.
And then see, maybe time it, use the
time function at the command line,
or whatever, on a mac or a Linux.
I'm not sure what it is PC.
RODRIGO DABOIN SANCHEZ:
The current version
is still using a list for the searched.
COLTON OGDEN: Right.
Yeah, so you'd have to
switch it to the set version
in order to see what that
performance difference would be.
Virani says, too bad it's too late.
Maybe I'll watch the VOD,
referring it to tomorrow's stream.
Yeah, definitely watch the VOD.
All these videos go up on to YouTube.
I'm going to very tragically transition
as back to the full shot here.
We're going to say that
maybe this won't actually
finish by the time the stream is over.
Ah, geez.
BeastKris, "turned in too late today."
Don't worry, the video is
going to be on YouTube.
So it'll be on YouTube
within a couple hours.
You'll be able to see, watch it in
its entirety as all of our videos are.
And what I was going to say was if
you are watching this on YouTube,
you can tune in to CS50.
It's Twitch channel on twitch.tv/CS50tv.
Every week we have programming
videos from scratch.
I do game programming.
Rodrigo came in today to do some Python.
David's going to come in tomorrow
to also show us some Python stuff.
And more I guess to talk about making
applications using pre-built software,
because we did use a lot
libraries for that application.
But yeah, a pleasure as always.
Thanks everybody who tuned in today.
We'll catch you tomorrow at 3 PM Eastern
Standard time with David for render 50
so thanks again Rodrigo
you for coming in today.
RODRIGO DABOIN SANCHEZ:
Thank you for having me.
COLTON OGDEN: Much appreciated.
We will see all of you later.
Have a good rest of your day.
