Lets say we have the Tic-Tac-Toe game at hand. And we want
to precompute a winning strategy in the following way as a tree.
From the winning moves only one is select and stored in the tree.
The many loosing moves the opponent has, all are stored in the
tree, so that we can blindly use the tree to guide us to our
next winning move, which is than again only one in the tree,
/
o---*- ..
\ /
o---*- ..
\
and so on, one, multiple, one, multiple etc.. How would one
do this in Prolog so that computing one such tree can be done
quite quickly for Tic-Tac-Toe game and a given start configuration?
I would do it like this (adapting from min max):
My choices are white nodes, other player's choices are black nodes.
Form a solving tree depth-first.
If the game has ended mark this leaf as won, tie or lost.
If a black node is not a leaf and all of its children are marked: "keep" all childnodes, mark the node as the worst cenario from its children (lost before tie before won)
If a white node has only one child left: copy the marking.
If a white node has one marked child which is maked as won, ignore the left over unmarked child-nodes (no traversing necessary). Mark as won.
If a white node has two marked children: keep only the best child, prefer won over tie over lost. If the mark is won or there are no unmarked nodes left the 2 above rules hold. Otherwise traverse an unmarked child to repeat this process.
the marking is only important for the minmax, not for the choice-tree.
for every childnode store the move to reach it
form the final tree as nested list (while traversing the tree)
Output would look like this:
[[1,
[2,[4,
[3,[..],[..]],[5,[3,[..],[..],[..]]]
]],
[3,[4,[5,..],[2,..]]],
[4,..],
[5,..]
]]
I start and have only one option: using move 1. Then black has the options 2, 3, 4 and 5, where these are all possible moves for black. If he chooses 2, I choose 4. Black now has option 3 and 5. If he chooses 5, I choose 3 and so on. Be sure to have enough RAM available ;)
Here is some proof of the pudding. One can use aggregate_all/3 for min/max, here is a little adaptation of the code here. But the code below does not yet return a winning strategy. It only returns a first move and a score:
% best(+Board, +Player, -Move, -Score)
best(X, P, N, W) :-
move(X, P, Y, N),
(win(Y, P) -> W = 1;
other(P, Q),
(tie(Y, Q) -> W = 0;
aggregate_all(max(V), best(Y, Q, _, V), H),
W is -H)).
We can check the moves and scores we get for a position:
?- best([[x, -, o], [x, -, -], [o, -, -]], x, N, W).
N = 2,
W = -1 ;
N = 5,
W = 1 ;
N = 6,
W = -1 ;
N = 8,
W = -1 ;
N = 9,
W = -1
Now how would we go about and store a winning strategy and choose a winning strategy? One idea here is now to replace the aggregate_all/3 by a findall/3. This should give us the multi branches of a winning strategy:
% best(+Board, +Player, -Tree, -Score)
best(X, P, N-R, W) :-
move(X, P, Y, N),
(win(Y, P) -> R = [], W = 1;
other(P, Q),
(tie(Y, Q) -> R = [], W = 0;
findall(M-V, best(Y, Q, M, V), L),
max_value(L, -1, H),
(Q == x ->
filter_value(L, H, J),
random_member(U, J),
R = [U];
strip_value(L, R)),
W is -H)).
We use random_member/2 for the single branches. Rerun to get different correct results:
?- best([[x, -, o], [x, -, -], [o, -, -]], x, N, W), W = 1.
N = 5-[2-[6-[]], 6-[9-[]], 8-[9-[]], 9-[6-[]]],
W = 1 ;
No
?- best([[x, -, o], [x, -, -], [o, -, -]], x, N, W), W = 1.
N = 5-[2-[8-[6-[9-[]], 9-[6-[]]]], 6-[9-[]], 8-[6-[]], 9-[6-[]]],
W = 1 ;
No
Open source:
Prolog code for the tic-tac-toe game
score via aggregate, return first move
https://gist.github.com/jburse/928f060331ed7d5307a0d3fcd6d4aae9#file-tictac2-pl
Prolog code for the tic-tac-toe game
score via findall, return random winning strategy
https://gist.github.com/jburse/928f060331ed7d5307a0d3fcd6d4aae9#file-tictac3-pl
I have two vectors with equal dimensions and need to find the distance between them
I have tried various approaches:
sum([a-b for a, b in zip(u, v)])
c= sum([a-b for a, b in zip(u, v)]
#If x is negative, multiply by negative one to convert x to a positive
if c<=0:
return c*-1
#No changes are made to x if it is positive
else:
return c
I am yet to have success!
You want to use the abs() function, which is available in standard python.
So if you have
a = [1,2,3,4,5,.4]
b = [4,3,4,5,-2,.8]
Than you can get the distance with
sum([abs(i-j) for i,j in zip(a,b)])
We can use the sklearn implementation to check indeed this is the correct answer.
from sklearn.metrics.pairwise import manhattan_distances
manhattan_distances([a], [b])
I am trying to learn sympy by going through some textbook problems.
I have one that is asking to get a formula for Rankine in terms of Kelvin.
This is easy to solve without simpy (given the formulas):
(π=πβ459.4, π=5π/9β1609, π=π+273)
With some algebra, π=5r/9
But I do not know how to get solve explicitly for k in terms of r with simpy. I can have it solve the system of eqns but not sure how to specify for which variable in terms of which.
My attempt:
import sympy as sp
r, c, k, f = symbols('r c k f')
eq1 = sp.Eq(f, r-459.4) # f=r-459.4
eq2 = sp.Eq(c, (5/9)*f-(160/9)) # c = (5/9)*f-(160/9)
eq3 = sp.Eq(k, c+273) # k = c+273
ans = sp.solve((eq1, eq2, eq3), (r, c, k, f)) #3 eqns, 4 unknowns (f, r, c, k)
ans
yielded
{π:0.555555555555556πβ17.7777777777778, π:0.555555555555556π+255.222222222222, π:π+459.4}
I think I found it out using linsolve instead
import sympy as sp
eq1 = sp.Eq(f, r-459.4) # f=r-459.4
eq2 = sp.Eq(c, (5/9)*f-(160/9)) # c = (5/9)*f-(160/9)
eq3 = sp.Eq(k, c+273) # k = c+273
sp.linsolve([eq2, eq3], [k,c]) #{(0.555555555555556π+255.222222222222, 0.555555555555556πβ17.7777777777778)}
sp.linsolve([eq1, eq2], [c,f]) #{(0.555555555555556πβ273.0, πβ459.4)}
sp.linsolve([eq1, eq2, eq3], [k,c,f]) #{(0.555555555555556π, 0.555555555555556πβ273.0, πβ459.4)}
I'm trying to see if I can get Alloy to return the largest possible answer for a particular set. So, in this example, I would like the answers x={}, x=A, and x=B to not be generated by the model finder.
abstract sig X{}
one sig A extends X{}
one sig B extends X{}
pred(x: set X) {
x in A + B
}
I have tried something along the lines of:
pred(x: set X) {
x in A + B and
no y : set X |
y in A + B and #(y) > #(x)
}
but I get an error that analysis is not possible since it requires higher-order quantification.
I was wondering if there is a possible (or simpler) way to do this?
Alloy does not currently have any built-in functionality to produce maximal or minimal solutions. And yes, you're right that specifying that a solution is maximal generally requires higher order quantification. However, you can ensure that the solution is at least locally maximal with a first order quantification:
pred p (x: set X) {...}
pred locally_maximal_p (x: set X) {
p(x)
no e: X - x | p(x + e)
}
How does the 'canonize' function (given below, from Ukkonen's paper) work, and in particular, when is the while loop finished? I think the value of p' - k' will always remain less than that of p - k. Am I right or wrong?
procedure canonize(s, (k, p)):
1. if p < k then return (s, k)
2. else
3. find the tkβtransition g'(s, (k', p')) = s' from s;
4. while p' β k' <= p β k do
5. k = k + p' β k' + 1;
6. s = s';
7. if k <= p then find the tkβtransition g'(s, (k', p')) = s' from s;
8. return (s, k).
What the canonize function does is what is described at the very end of this SA post, where we consider a situation like this:
The situation is this:
The active point is at (red,'d',3), i.e. three characters into the defg edge going out of the red node.
Now we follow a suffix link to the green node. In theory, our active node is now (green,'d',3).
Unfortunately that point does not exist, because the de edge going out of the green node has got only 2 characters. Hence, we apply the canonize function.
It works like this:
The starting character of the edge we are interested in is d. This character is referred to as tk in Ukkonen's notation. So, "finding the tk-edge" means finding the de edge at the green node.
That edge is only two characters in length. I.e. (p' - k') == 2 in Ukkonen's notation. But the original edge had three characters: (p - k) == 3. So <= is true and we enter the loop.
We shorten the edge we are looking for from def to f. This is what the p := p + (k' - p') + 1 step does.
We advance to the state the de edge points to, i.e. the blue state. That is what s := s' does.
Since the remaining part f of the edge is not empty (k <= p), we identify the relevant outgoing edge (that is the fg edge going out of the blue node). This step sets k' and p' to entirely new values, because they now refer to the string fg, and its length (p' - k') will now be 2.
The length of the remaining edge f, (p - k), is now 1, and the length of the candidate edge fg for the new active point, (p' - k'), is 2. Hence the loop condition
while (p' - k') <= (p - k) do
is no longer true, so the loop ends, and indeed the new (and correct) active point is (blue,'f',1).
[Actually, in Ukkonen's notation, the end pointer p of an edge points to the position of the final character of the edge, not the position that follows it. Hence, strictly speaking, (p - k) is 0, not 1, and (p' - k') is 1, not 2. But what matters is not the absolute value of the length, but the relative comparison of the two different lengths.]
A few final notes:
Pointers like p and k refer to positions in the original input text t. That can be quite confusing. For example, the pointers used in the de edge at the green node will refer to some substring de of t, and the pointers used in the fg edge at the blue node will refer to some substring fg of t. Although the string defg must appear as one continuous string somewhere in t, the substring fg might appear in other places, too. So, the pointer k of the fg edge is not necessarily the end pointer p of the de edge plus one.
What counts when we decide whether or not to end the loop is therefore not the absolute positions k or p, but the length (p - k) of the remaining edge compared to the length (p' - k') of the current candidate edge.
In your question, line 4 of the code snippet, there is a typo: It should be k' instead of k;.
I had to change the canonize function because it didn't handle the auxiliary state properly. I added the following code after the 'p < k' if:
if (s == auxiliary)
{
s = root;
k++;
if (p < k)
return;
}
It seems to work now :)