>> So today we're going to learn
more about applications of Prolog
and that's also the theme
of the homework, of course.
So the reason that I did that is I was Googling
around for something with Prolog and I came
across some discussion somewhere and read it
or somewhere where one person denoted the fact
that he was tortured with Prolog in
his programming language's class.
[Laughter] And all I could remember is
that you can do the append in Prolog.
That was the only thing that it was good for, he
thought, and so I thought that was said and want
to know that he can do other
things then with Prolog, right?
So the first thing here is
this nifty puzzle here.
I developed this puzzle because I had another
puzzle in a previous some work assignment
and then I Googled for it and the
very first thing was the solution
to that homework assignment.
So I guess I can't use that again.
And the next thing was this puzzle because
it also has to do with flipping stuff.
And it's pretty nifty.
So what you'd have to do is
you'd click on one of these tiles
and then it flips and [inaudible].
And your mission should you choose to
accept it is to make all of the tiles red.
And so you might say and I'm
going to flip here and here.
Kind of -- well maybe not so clever.
Let me undo that.
So maybe here, right?
And here by now, we seem to
be kind of getting stuck.
>> Professor, [inaudible]
mouse so we can see it better?
>> Sure. Sure.
Okay.
>> Can you [inaudible]?
>> Yes. In fact, I'm going to
reset it and give you the solution.
>> Did you just memorize it?
>> Oh absolutely, yes.
>> Uh oh.
>> Well okay [clapping].
So how do you do it?
So it turns out that and it's
easy to see that the order
in which you make the clicks
is completely immaterial
because what it should does
is it flips those tiles
and in which order you flip them
in because it doesn't matter.
What matters in the end is that each
tile is flipped an odd number of times.
If the order doesn't matter, we might
as well choose an order that's
easy for us to reason about.
So let's do row by row.
First row then the second row then
the third row and then left to right.
Why not? So now one has to look at all the
various possibilities for the first row.
And so let me say I'm clicking this one here
and this one because it seems like a good idea.
Now from this point on, everything
is automatic, why?
So I'm done with the first row.
That was to set it up.
Then in the second row I have to fit exactly
where there is a blue one in the first row
because if I don't flip it,
it'll never get flipped again.
Remember, we go left to right, top to bottom.
And I shouldn't flip anywhere because
I wouldn't be able to undo it later.
So I flip exactly where the blue ones are.
So I flip exactly where the blues ones are.
Now I need to flip everywhere.
And it didn't work.
Right? That didn't -- I can no longer fix these
two because I'm done with all of the flipping
since I've committed to doing it
left to right and top to bottom.
So that means that flipping, that
starting off with position of 1
and 5 does not give me a solution at all.
So it turns out, in fact,
that for a 5 by 5 puzzle,
there's only two possible starting positions.
Namely clicking 1 and 2 or
symmetrically clicking 4 and 5.
And so that's what your program
is going to figure out.
Now how you're going to figure it out.
It's -- there's two distinct
tasks that need to be done.
One is if you have the flips in a given row,
you want to find out how's the next row going
to look like when you make the necessary flips.
We know what the necessary flips are.
They're in all the blue places but you need to
figure out then what does that row look like so
that you then know what are its blue places.
And then one repeats that n minus
2 times or n minus 1 times maybe.
And then we get the last row and if the last
row is completely flipped then you may have
a solution.
And now you need to plug
in all the possible ways
of getting the first row flipped
and that's all you need to do.
So it's a few lines of Prolog.
The hardest thing is the
flipping but it's not super hard
because after representation that I've chosen.
So what I've told is that, you know,
make it so that you collect the flips
in the first row in a list of column numbers.
Then add the list for the next row and so on.
And that turns out to be a
good representation to use.
I'll talk about that a little bit more.
So that's [inaudible] and it
is, you know, surprisingly nifty
that you can get all of the solutions.
It's also interesting how many there are.
So when you try different sizes of the
rectangles, you're going to get different ones.
So sometimes there's one.
Sometimes there's two.
Sometimes there's four.
You might want to find one
where there's 8 or 16.
And extra credit if you can explain
to me why it's always the power of 2.
That means you would have paid
attention in your algebra class.
All right.
The next one is this more intricate puzzle.
So it's this Rubik's like cube
that -- this thing over here.
So [inaudible] actually managed
to solve it yesterday
with all of the various twists and turns.
So it's not like a regular Rubik's cube.
You have to twist it like in this diagonal way.
Each of these vertices, you can
rotate it 120 degrees back and forth.
And then its mean that is has this little
color markings and they all have to match
and so it seems more complicated
than a Rubik's cube.
And so how can one solve one of these things?
So I will give resolve not to watch
videos of which there are many.
So like here is a crazy dude
who shows how to do it.
And so I just did not find
this all that illuminating.
[ Laughter ]
[ Foreign Language Spoken ]
So yeah he goes on for 30 minutes and so
you know, there's a whole bunch of them.
>> [Inaudible] algorithm.
>> No I think she has a different algorithm.
>> Okay if you have the red one
up top but none of the centers --
>> And again, yeah, it's not clear to
me why it was always going to work.
So I figure, you know, we should
be able to do our own algorithms.
And so the key to doing that is to try to come
up with the turns that don't do too much damage.
And it's a very confusing thing because
everything seems to turn together.
So if you have an extra 12 bucks to
spare, just go ahead and order one.
It is a bit of an experience.
So we want to find out sequences of rotations
that preserve as much stuff as possible.
So I didn't know whether this rotations
exist but then after writing this program,
I did discover that you can do the following.
You can make it so that all 8 corners stay where
they are and the faces get moved in some way.
And it turned out not to be it's not that
hard to just by hand, you can eyeball it.
Put 4 corners in the right place
which interestingly enough,
automatically puts the other
4 corners in the right place.
They're going to be twisted but that's okay.
One fixes that at the end.
So you're going to have to -- the
corners are going to be right.
Position may be twisted.
Again, the faces are not going
to be in the right position
but you can discover enough moves
that fix the corner positions.
That leave them exactly where they are
and [inaudible] the faces in some way.
That enough of them that any of the
possible permutations can be so generated.
So I have a Prolog query that if the faces are
messed up, I put in the permutation that I need
to get the faces repaired and it
spits out a sequence of usually 5 or 6
or 7 rotations that I need to execute.
I execute them and the faces
are then in the right position.
At that point, I have both the faces
and the corners in the right position.
And then there is the little matter of twisting
the corner so that they're correctly oriented.
The little colors are usually in the wrong way.
That turns out to be exactly the
same mechanism as the flip game.
The first part that you're doing
and I can compute a set of vectors
that show how the basic rotations are
and then [inaudible] linear equation.
And I have a little program for that as well.
That way I get all of the
corners correctly oriented.
And then it can happen that 2 or 4 of
these faces are in the wrong orientation
at different -- at 180 degrees and then
there is a similar way of repairing this.
And so I can do it with, you know, these
moves that do as little damage as possible
and eventually it could probably faster
[inaudible] and have a YouTube video.
[Laughter] Anyway.
But so the fun part is to [inaudible]
to help out with these computations.
And then where Prolog has
been really good about is
that you can do all sorts of different queries.
I can say compute all the possible rotation
sequences of links 4 and 5 and 6 and 7 and 8.
So that the corners are unchanged or so
that I get the following flip that I need
to get the faces in the right order.
And they're fairly straightforward queries.
The hardest part is to figure out what is
cumulative effect of a bunch of rotations.
So each of the rotations that you can do --
all you can do is you can
take one of these 8 corners
and you can then make it twist
in one direction or the other.
And it turns out that it twists -- you only
have to worry about twists in one direction
because if you do that twice it's the same
way as doing it the opposite direction.
So we're only going to look at
positive twists and you know,
if you have to do r 0 followed
by r 0 that's the way it is.
And so how does one figure
out the cumulative effect
of doing say r 0 and then r 4 and r 3 and so on?
Well the permute the corners
and they permute the faces.
So if you like the permutations
in cycle notation.
Here's, for example, what r 3 does.
It sends corner 1 to corner 2, corner 2
to corner 7, and corner 7 to corner 1.
When you have two cycles, you can figure out
what is the effect of doing one after the other.
So I go through that here.
Again, we're sending 0 to 3 and then
the next cycle sends 3 back to 0.
So it happens to not do anything on the 0.
And it takes the 3 to the 5 and
then the 5 is left alone here.
And so if you follow through with the
four numbers that you can find in here,
then you can find what's
the product of these two.
What is the effect of doing
one and then the other?
Now sometimes you have two cycles
where the product is not just one cycle
but where the product breaks
up into two different cycles.
There's a general rule for that that I
hope that they taught you in Math 42.
That if you have any permutation at all, then
you can write is a product of disjoint cycles.
>> So is that [inaudible]
first do the permutation
on the left and do the one on the right?
>> That is the more convenient way of doing it.
>> Okay. And do you do the right
permutation on the results?
>> Yes.
>> Okay.
>> Yes. Yes.
So you have -- that's why I say
you have this case, for example.
If you [inaudible] the 0
gets sent by the first cycle.
It gets sent to a 3.
And then where does the 3
get sent by the second cycle?
It gets sent back to the 0.
>> So in the right permutation, is 0 referring
to the resulting element
maximization [inaudible] 0 went to?
>> Well this is the rotation.
This is 0, 6, 3 that is r 2.
It is whatever r 2 does.
When r 2 is this one here and
if previously something got
in there, then r 2 will permute those.
That's how permutations work.
So again I have to figure this out.
And it's not really hard to write
a Prolog predicate that computes
where a given cycle sends a given element
because it doesn't need to look -- it --
either it's a neighbor and they actually use a
predicate for neighbors in the standard library.
And/or it sends the last one to the first.
So your program does two cases and then
if you want to apply multiple cycles,
then you have to kind of figure out what
are all the possible elements and you find
that by a flattening out and sorting.
And so then you go through each of
them and asking where does it get sent
by dealing with one cycle at a time.
There's a bit of a recursion or map or whatever
followed -- whichever way you want to do it.
So I'm, you know, giving you a couple of
tips but there are many ways of doing it.
Do it by hand so that you know what you're
doing and then implement that in any old way.
Once you have that, then really all
that remains is to pick some number
of rotations and then apply them all.
And so I've given you, I think, the
picks, all of the possible sets of size
with repetition and out of a given list.
And because I couldn't find it in
the standard library for some reason.
And then now we want to --
we have the 8 permutations.
We have a target that we want to reach.
For you that target is going
to be that you don't want
to have anything happening on the edges.
Sorry on the corner.
So the corner permutation is the empty list.
Then you want to know which pick
is the one that makes it work
and n is the number of rotations
to put together.
So if you want to say can I
reach this particular target
with 5 rotations or with 6 rotations?
Then you put in a 5 or a 6 and
I mean I put in all of them.
And so this gets you all of the
possible picks of repetition.
Then this -- loop through them and
then you ask if I take this list,
this [inaudible] rotation
does it reach the target?
That's the procedure that you wrote before
and that's all you need to do to find the ones
that have this specific property that
they leave the corners unchanged.
And then you need to do it again
to find the particular one.
I'm only making you find three because
these three suffice to do all the others
but when I run it myself, I actually
read off the cube what permutation I want
and ask it directly how do I do that?
So that's that.
And so hopefully that's not too difficult
but it took me a couple of days to do it.
So and you need a little bit
more high powered Prolog than --
to do it efficiently anyway
than what we had so far.
So today's lecture gets you to be more effective
with Prolog so that you can do more than append.
So the first idiom that you want to get is
how do you procedural thinking in Prolog?
So the way we've introduced Prolog
is that you have these clauses.
So this means H is true provided
that B1 and B2 and so on are true.
And that's declarative thinking
at its purist, right?
But you can use it to do procedural thinking.
You can read this in a different way.
You can say I want to compute H and I'm
going to do that by first computing B1
and then B2 and the B3 and so on.
And that's perfectly accurate because
that's really what Prolog does, right?
It tries to prove the same as
otherwise inside in sequence.
So as an example, remember when
we wanted to solve the factorials.
What we wanted to find a number, n,
so that its factorial is m. Well,
that's what we wanted to be able to do, right?
Now how do we get there?
So now before that, we put the
number of steps that you need.
So we're going to generate a number
between 1 and n or 1 and m here.
We're going to take each of them in
turn and then you're going to try
out whether the factorial relationship holds.
So you can think of it as a step-by-step cycle.
Generate a number.
Pick one. Plug it in.
And then that procedural
thinking is pretty easy.
So if you're in a case where that
works, you know absolutely go for it.
It'll -- it's an accurate
reflection of how Prolog works.
And it always work really well if the solution
to the first step is the input to the next step
and the solution of the next step
is the input to the third step.
So whenever you can articulate a procedure
that says do this then do that with a result,
then do that with a result, and then
you know, check whether it's right.
Then Prolog is perfect for that.
Yes.
>> [Inaudible] solving a differential equation?
>> I -- yes exact -- well in one respect
has a lot more [inaudible], right?
I mean it's -- it has [inaudible].
[ Inaudible ]
So yeah, you know just [inaudible]
really try absolutely every possibility.
So if you think about the tile puzzle
with the flipping of the tiles.
It will try every possible
[inaudible] in the first row
by simply saying let me first
generate all those counters which,
by the way, you did in the last homework.
Then you plug it in.
Then you ask does it work?
So in the previous slide, we've taken
away this member to move through a set.
When you recursively need to break
something down, there's a variation
of member that's more powerful called select.
That's really very, very useful.
Anytime that you have to do something recursive.
So select X, A, R is true if X is
an element of A and R is the rest.
In fact, you've implemented that
language in the last homework.
We called it without.
And so you can think of it procedurally by
saying go through all of the elements X of A
and then subtract it from X [inaudible] R.
But that's a very useful
way of thinking about it.
So [inaudible] of select so let me see that
but it is really, really useful when you want
to recursively pick an element, do something
with it, and then deal with the rest.
So here's the poster child application.
I want to find all of the
permutations of list A.
So I want B to be a permutation
of A. What does that mean?
Somebody you select and saying I'll take
an element X from A and store the rest
in R. Then permute the rest into the
permutation S and then stick X in the function.
That gives me all the permutations
because over here, then if --
that says do this for me, all of the x's.
Over here is an implied loop that
says give me all the permutations.
And then let's put them back together.
So that's something that, you know, you
want to look at as a model when you need
to do something similar and that'll work when
you need to do something with [inaudible].
So for all can always be done
by using select or member
that way you can iterate over a -- the set.
And you want to use select if you need the rest.
So in this case, you can actually
make a small simplification
that you may or may not actually want to do.
So notice over here, I say B is X followed S.
And you can actually plug it right back in.
You don't even have to have a B. So if you
wanted to make your things look more concise
and more elegant, you can have -- anytime
you have [inaudible] like this, then equals.
You can always eliminate it because
equal just means this thing unifies
with that and you just plug in it.
That's how Prolog does.
Remember, all that Prolog
ever does is [inaudible].
All right.
Another useful tip.
If you need to debug something,
you can, of course, use the tracer
but not everyone is a fan of the tracer.
It -- alternately you can
put in print statements.
So the way you do it by using writeln.
Writeln prints X and then keeps on going.
So over here, for example, I have an example
of where I'm generating the
permutations in an inefficient way.
Where I'm saying to permutate from A
to B I say well I'm selecting X from A
and then I get a rest, R, and then I stick X
and R together in a different way to get B.
And that'll work but it gives
me some of them more than once.
And if we want to find out why, then I can
put in a writeln in here and then I get all
of the Rs printed out that occur at this point.
So that's a simple and effective
way of getting some insight
as to what kind of looping Prolog is doing.
What kind of things is it trying [inaudible].
So keep that in mind.
Now as I'm sure you've all
seen in the last homework.
It is pretty easy to get a stack
overflow by making an infinite search.
So as an example, the first time that I tried
to implement that lab in the Game of Nim
where it says in the last specification.
It is so very easy to check whether T
is at least one less than F. And it's --
and so when I first did it, I said well so
I want to move from F models to T models
if T models has, at least, one less or
on the other hand, if I can't go from T
to something that's one more and then I've
had even more than that and then I can get it.
And I got the right solution.
So this is the only first half of the
predicate not the one with [inaudible].
So I got 0, 1, 2, 3, 4 which
are, you know, one less.
And then I got a sample from.
And so the reason for that is that
I've asked it to do an infinite search.
So you want to be able to look out for that.
So it's what do I know?
I know the [inaudible] and I
want to know the [inaudible].
I don't get to know the T but at this point,
I'm saying give me all the Ts which I don't know
and compound it with one model and all
the S. I don't know the S's either.
So in here, there's infinitely
many possibilities.
So you never want to get yourself into that.
You always want to be in a
position where the next step works
on a constrained set of possibilities.
In this case, it's easy enough to do.
All you do is you flip these two
because the second [inaudible] here.
F is given.
So the F is given here and so then there's
only a finite number of possibilities
of decomposing it into S and [inaudible].
That way it's finite.
So you put it first, then the second, and it
works on a finite set and all will be well.
So at any time you get a sample
of [inaudible], ask yourself,
where did you permit an infinite
set of possibilities to be examined.
And if you're not entirely sure,
then you ask the other question.
You say is this thing finite?
Is this thing finite?
And it's easy to see if something is finite.
You know for sure that if n is finite, it's
the last thing is given if you already know.
There's a finite number of
possibilities to do the [inaudible].
The same thing with member.
Member is finite is you're
exploring a finite set or select.
So that's a useful thing to do.
The other thing that's really important
is to pick a good data representation.
So for -- I have one with the assignment
where you play a game with a set of 10 dice.
And then you have to do something with the dice.
And I think you have to flip them actually.
And how do you represent that?
So those 10 dice [inaudible] interesting.
If they get flipped, then something happens,
and then the next person's
[inaudible] turn whatever.
You could just have a list of
10 numbers between 1 and 6.
Maybe it's useful to sort them.
There is a whole bunch of queries
that give you sorted results.
And it depends on the game.
Maybe it's useful.
Maybe it's not useful.
You could use a map.
You could use a list of pairs where the first
one is the die and the second one is the count.
Do you really need the 1, 2, 3,
4, 5, and 6 in the beginning?
You could drop them and just make a frequency
table where this is the number of 1s,
the number of 2s, the number of 3s, and so on.
And any of those might be a good representation.
It really depends on what
it is that you're doing.
And so that is really kind of key.
So if you look at this puzzle here.
So the representation that I chose was to say
a list of column positions for the first row,
a list of column positions
for the second row, and so on.
And that turned out to be a really inspired
choice as I realized when I had to figure
out how to get the clicks because I
have what the second row looks like.
So I have -- it's red in positions 1 and 2.
Where do I need to make the clicks?
In 3, 4, 5.
And to complement of what I have.
Well, it's trivial to do a complement
like because all you ask is, you know,
make a union out of the two to get
what [inaudible] numbers and in fact,
there's must be a predicate for
complement in the standard library.
So that made it pretty easy.
I've given you a link to an
unhappy stack overflow query
where they did something different.
Their representation was to choose a list of, in
this case, 25 integers that would then be 1, 2,
3, 4, 5, 6, 7, 8, 9, 10, and so on.
And then to compute the neighbors
of these elements using minus
1, plus 1, minus 5, and plus 5.
And then shoot, like the whole thing looks
like a complete and utter mess in the end.
So it's having a good data
representation really is half the battle.
So similarly with the permutations,
I struggle with that.
When I first looked at using this kind of
complicated to put the cycles together,
I said maybe I have the wrong
data representation.
Maybe what I should do be doing is represent
them so that I have pairs from [inaudible].
And actually then the composition was
pretty easy but it got messy elsewhere
and then I went back to the cycles and
I figured if mathematicians have done it
for 200 years maybe it's not so bad.
So that is a key thing to
be successful in Prolog.
Another thing that is really useful
and I've used that several places
in the homework is to use
higher-order predicates.
So just like in a functional
programming language,
you can use functions that consume functions.
Prolog has predicates that consume predicates.
You see one in -- if you've looked at the
grading stretch for homework 9 where [inaudible]
or I might have used [inaudible].
Find all a set of similar.
So they find all solutions for a query.
So here I say this directly
from the homework page
but find the all X such that something is true.
In this case, that X is a
subsequence of a, b, c, d, e, f,
and collect all the answers in a sentence.
So that if you ever need the solution
of a predicate, you know [inaudible].
Of any query.
If you need the solution of any query
in a set, find all and just put it in.
Now you often don't need it because if you
wanted to just do something with them and go on,
then you could just use subseq without the
find all followed by a member or select
and that way you also do -- but
sometimes you do [inaudible].
Find all has a couple of variance.
If you want to collect multiple answers,
you then collect lists like that.
Lists of variables and then your query has
to have more than X and Y in there as well.
And then you get a list of pairs.
A list of list [inaudible] languages.
If your query is composed of more than one
statement, then you put them in parenthesis.
So here I want to find all X such as X is
member of L and X is not a winning position.
Now in [inaudible] we have things like filter
that we can include only things match a
certain predicate or that is a filter not
for excluding the ones that match the predicate.
So I needed to use that in one spot in
homework 10 where when you compute the product
of cycles, you get a list of other cycles.
And you want to throw away
the ones that have length 1
because as a cycle, oh length 1 does nothing.
Like here in the cycle 5 maps 5 to itself.
So I might as well not have it.
And how do you throw those away?
The simplest way is to use exclude.
Exclude takes a list and a predicate
and then it throws away all the ones
that fulfill the predicate.
So you can make yourself a little
predicate singleton where you say
if something is a singleton
if has only [inaudible] 1.
Okay. And this way you exclude
them all of the singletons.
Now you don't even have to make a predicate.
You can use a lambda.
Here's how you write a lambda in Prolog.
You say exclude [inaudible], the
thing in red, as the predicate.
And so it is a -- the lambda is
written with a greater [inaudible].
So we have the function that takes
an X and then that that is true
if X can be unified within [inaudible].
Yeah?
>> Why did you change the order
of the inputs when you did lambda?
>> Oh maybe I missed -- I'm wrong.
One of the two must be wrong.
>> Okay.
>> Yeah. Yeah I'll need to fix the slide.
So [inaudible] exactly the opposite.
It keeps the ones that match the predicate.
So include is the exact same thing as filter.
So if you feel that you want to filter, then,
you know, include is your friend if you want
to filter or not the next
clue but if you have filter,
of course you want to map as well and you do.
So if you're ever in a situation where
you have a list of things and now you want
to apply a function through them.
Then you kind of can.
Then [inaudible] functions
[inaudible] but there are predicates.
So to give you an example
that I used in the homework.
I sketched -- I might have get a list of
permutations like this and then when I look
at it I say well I don't know
how twist a cube like that.
I want to know the labels of these permutations.
So I made myself a binary predicate
name where the input is the permutation
and the output is the name or the other way
around whichever way you want to look at.
It's a predicate and that is map.
So by mapping the name function, then
it's applied to the first element
and then the second element and I
get this list of rotation pairs.
And so anytime that you have
a function that you want
to apply towards the elements
of a list, use map.
Map has always been a good thing
and you have it [inaudible].
The other thing, you even
have fold if you want it.
So if you wanted to write an arbitrary loop, you
can use fold and the way that you know and love
where you need to provide a function
that is applied at each stage.
So you have an example where I'm adding
up all of the elements in a list.
You don't need to do that.
There is a functional sum list that
does that but let's say that wasn't.
Then I can use fold.
I make an add predicate so the sum is
the sum of X and Y. And then I have list.
I have an initial element for the fold
and out comes the sum which in this case,
you know we then compute 0 plus 1 plus 2 plus 3.
If I don't want to make a predicate, I can
also make lambda that -- let me see here.
In this case or be it a [inaudible] predicate
because I need to have the input from the list,
the input from -- the X's that the left fold.
So the X is the list element.
The Y is the intermediate value and then
you just compute the answer here by hand.
So either way you have all of the
higher-order functions that you're used
to from functional programming
available for you in Prolog.
[ Inaudible ]
>> Yeah.
[ Inaudible ]
>> Yeah.
[ Inaudible ]
So final takes as three of [inaudible],
a variable or list of variables,
a query with a variable [inaudible]
and a thing that holds the answer.
And it does exactly what it
says, it finds all the answers.
So the last recommendation
is know The Standard Library.
The documentation is not exactly pretty.
It's definitely -- been caught in a time warp.
So that's what the web looked like in 1985.
And yeah, I mean let's open it up.
So but we can find your way around.
So there is a whole bunch
of useful list operations.
You know any list operation that
you can imagine does probably exist.
Like I've used flatten in the homework.
Flatten does -- what do you think it does?
It has a list of lists and
flattens it into just a single list.
There's a way of sorting something.
So there's the notion of ordered sets.
You can have [inaudible] intersection.
I should have put sort on
here because sort is useful.
If you needed to deal with
frequency tables, there is a function
that -- the set of functions from maps.
Mapping -- maps in the sense of key value pairs.
We've talked about the accumulators
and higher-order predicates.
When you look at The Standard Library and look
at a particular function, let me show one.
Then you'll notice that there are these
various -- it says question mark element.
Question mark list.
Here's a plus list of list, question mark list.
And what these things mean is --
A question mark means that thing
could be an input or an output.
A plus means this can only be an input and
a minus means it can only be an output.
And so that's obviously, you know,
something that's useful to know.
Sometimes you see a colon G and that's use for
higher-order predicates so that is [inaudible].
So that means it's some predicate.
So when you look at the documentation for
include or exclude or a map you'll find things.
So that's really all you kind of need
to know to be dangerous in Prolog.
And so we're going to try
this out with a simple lab.
So I found this game here when Googling
about called the Thirty-One Game.
The way you play it is that you have 24 cards
by just grabbing the cards; Ace, 2, 3, 4, 5,
6, and 6 from a standard card game.
And each player turns over
a card of their choice.
And so I'll just put player
A might turn over the 3.
Player B might turn over the A.
The first player who makes the value of
the turned over cards go to over 31 loses.
So you do not want to be the first one
to go from 31 or below to 32 or above.
So that's the game.
Now, of course, what we want
to do is we want to make it
so that the computer can play it perfectly.
And for that, you know, we need
to do things like figure out how
to move from one game stage to the next.
So what would be a good question?
What would be a good representation
of the game stages?
Like we've done representations of the
Nim game, with pebbles, and numbers.
And here again, think of three different
representations of this game stage.
Then here I'm asking you to compute.
How do you compute the value of a game state?
Here I'm giving you a particular representation
and then I want you to compute the value
because if you did this in Java, you
would write a for with an integer index.
And let's say you had something like that.
How could you simulate that in Prolog?
And so it's not hard to do that.
You would say well, you'd have to use
recursion and so you'd have to use this index
as a helper parameter somewhere and it gets
you -- guides you here on how to do that.
And then it says how do you compute possible
flips and it guides you in a way to do that.
And then finally we want to
actually play the game and win that.
So go through that and in fact, it's a good idea
for you to spend a few minutes playing the game
if I've brought enough card
games for all of you.
Yeah, so that way you get some basic
idea what it's about but again,
don't spend too much time away with that.
Do the coding.
And then we'll talk about it.
[ Background sounds ]
And the last 10 minutes going
through this together.
So I asked for three different
representations of the game state.
We have one on the next slide where we
have a frequency list where, you know,
this is the number of aces and there's no 2s.
There's two 3s and so on.
So what other representations
could one have chosen?
[ Inaudible ]
By building an array of 24 Booleans.
>> Yeah.
>> Okay. Yes.
>> So you could have two lists
of all the individual cards.
Ones for chosen and ones for not chosen.
>> Okay. So a list of cards for the
chosen and then the complement of that.
>> Right.
>> What else?
>> Oh the difference representation.
>> Yeah.
>> So you could have a frequency table
of available, frequency table of taken,
individual cards are available and [inaudible].
>> Yeah. Or you could have, just to
give another one, you could have a map
where you map each card name to the
number of cards that are already taken.
>> Oh.
>> For example, right?
So any number of different representations.
So I've pondered those and
I've said the frequency table
of the ones that are already turned over.
That's the one that I'm going to go.
So now we need to compute the
value of one of these states.
This thing here, for example,
would have value when this is 1.
So there's no 2's and there's two 3's.
So the value of this thing would be 7.
So in general, to go to value, you would --
if you had to do this in Java,
you would have a loop, right?
You would have index, I, that can multiply
with each of the values in this list
and then you would add them all up.
Now how you can do that in Prolog.
Actually one person in the previous section
said, you know, you don't really need a loop.
You could just say 1 times the first element
plus 2 times the second plus 3 times the third
and so on but that's very true
but I wanted to practice you know,
what if you do want to have a loop?
So you don't specifically have loops
but there are ways you can make them.
So here's one possible way of doing it.
Very pedestrian.
Maybe not super elegant but it definitely works.
So we say that we are going to make
a recursive function and we're going
to have a helper variable that I call S here.
Maybe I should have called it I
that gives the current position.
And so then I'm starting off
and say okay, so we're taking --
we're multiplying X, that's the head of the
list with S. That's the position number and then
when I want to recursively
call value on the tail
with S plus 1 and then I get an answer back.
And then I want to add that answer together
with what I get from the head, right?
So here is that.
So I call value of the tail, S plus
1 and I get an answer to call n 1.
Except I can't call S plus 1.
Why can't I put an S plus 1 in here?
>> Because it does not evaluate it.
>> Yeah, it wouldn't be evaluated.
It would then try to match
and then you would get
like 0 plus 1 plus 1 plus 1
plus 1 and would be no good.
So we force evaluation by putting
S plus 1 to the right hand side
of an is and that starts with an S 1.
And then we get an answer to n 1.
And then, the n that we want to have
is then, of course, p plus and 1.
So and you can [inaudible], right?
First compute this product.
Then okay, I have to do this silly increment.
Then I have my recursive call and
then I assemble the final answer.
>> What was n again?
>> N is the answer because
it's a predicate, right?
So you always need to have
a slot for the answer.
So that's really all that one needs to do here.
Now we want to compute possible
means that we can make.
So I have a list such as this one here.
I want to know what are all
the next moves that I can make.
So in -- we're not going to worry about
whether it's a winning or a losing state.
I just want to know what flips can I make.
Now I can change a 1 into what?
It can only go up, right?
And if you have like a small
number of possibilities,
it's often easier to just list
them all like I've done here.
And so these are all of the possible
flips than using arithmetic conditions.
You could do this with a condition and you
could say, you can move from n to the n 1
where n plus 1 and n 1 is less than 5.
You could do that but it is so much simpler
to just list all of the possibilities.
That way you never have any issue with Prolog
not being able to do arithmetic or guess work
because Prolog can truly try these all out.
So whenever there's a small
number of choices, do that.
When you're doing that in the
homework with the rotations.
I just have a predicate for what these
rotations are and there's 8 of them.
I gave them names.
I have the corresponding
vertex and face rotations
and so that's a really easy way of doing that.
So now -- so we want to be able to say how can
I go from a from list to a [inaudible] list?
And so, of course, what I want to do is I want
to grab one of these here and just flip it.
To flip it means, you know, if
it's -- is to do what it says here.
So in this case, I would,
you know, turn it into a 1.
That's the only possibility here.
So how can I go through a list and
say I want to flip each of them.
We kind of know how to go through a list.
You can use member or select.
But it doesn't put it back in the right place.
So there is an append trick that works for that.
And here it is.
So I say I'm going to find X sitting inside my
list F. So here's my list F and I'm finding X
by saying append A with a list that
starts with X and has other stuff
so that you catch the list F. That
means that X is sitting somewhere.
So then I'm going to flip it to
whatever it is that increases 1.
And then I'm going to put
it into the same space.
So that way I have edited in place.
Now that's actually a very
nifty way of doing it, right?
And I've now done that for
all of the possibilities.
So for all of the positions of X, I have now
applied the flip thing to it and added 1 to it.
So that's a nifty trick to know
anytime that you need to edit something
in place, then you use this append.
Now the rest of it is now
completely smooth sailing.
So I wanted to now define what are the moves.
Well the move needs to be one of these moves
and then the value needs to be less than 32.
So now I have the move predicate.
Then I can have a win predicate like we've
had in the games we've played before.
And now if you ask win nothing
is flipped over yet,
you know this is the game
position I have right now.
So the computer starts.
Nothing is flipped over yet and the --
so when you ask this it's going to take
a while and what's it going to say?
What do you think it's going to say?
>> It's going to take a very
long time to play [inaudible].
>> Yeah and then what it's going to say?
>> Back [inaudible].
>> No it actually didn't for me.
It said true.
>> Oh okay.
So you --
>> So that's it is a winning position, right?
But you know, that's nice but it
doesn't really tell us what to do.
So the way to do that is actually very easy.
Whenever you want Prolog to reveal something,
you just introduce another variable.
So and so I call that variable Y. That's
the next move that one needs to make.
And what is it?
So let's move X to Y and that's
the next move where it's not a win.
So this is exactly the same except
that I'm now revealing the Y
by sticking it into the predicate.
I also got tired of doing
the arithmetic by hand.
So I'm adding a third thing
in here and that is the value.
So let's try that.
So we're going to start playing with nothing.
And that takes a long time.
Now the computer wants to flip the Ace.
Okay. What do we want to do?
>> Flip the 6.
>> Okay. You want to flip the 6.
So at this point, we have the
Ace flipped and the 6 flipped.
And we'll see what the computer wants to do.
The computer wants to flip the Ace.
Okay. What do we want to do?
[ Inaudible ]
Okay. Here you want to try.
Okay I'm going to flip the 6.
Okay. Now the computer comes back really
quickly and wants to flip the Ace again.
It's going to run out of Aces pretty soon.
In the interest of time,
I will flip the 6 again.
And so it now wants to flip the Ace again.
>> [Inaudible] War Games.
The movie with the computer that
plays tic-tac-toe and like wages wars.
>> All right.
So now the computer wants to
flip the -- so I flipped a 6.
[ Inaudible ]
So then the computer flipped the 6.
Okay. And so now I lose.
Yes because I don't have an Ace to flip anymore.
So I have to flip a 2 and I get to be over 32.
>> Yeah.
>> All right.
So the computer wins.
And with what [inaudible], product, okay.
>> Possibly you could force it into a
losing position [inaudible] position?
>> Yes if you start off with a losing
-- so if I start then I'm going to --
I can force a win by simply flipping the
Ace because that's what the computer did.
So with this particular game,
you always want to start.
>> But [inaudible] win the
next move [inaudible] --
>> Well the program because it doesn't
want to print out all the whole entry.
So you have to do it a step at a time.
>> Oh.
>> Yeah it would be too tedious
otherwise to interpret the output.
All right.
So as you can see Prolog can
do way more than just append
and so I hope you enjoy the homework.
