]]>A few are curious about my Tic-Tac-Toe algorithm, so here it is :) First, I would like to give credit as well to Michael Macie. We designed this last year for a linear algebra project and it is very beautiful indeed :) I have modified it a bit to make it work even better with matrix row operations.

First: In Tic-Tac-Toe, there are 9 positions and 8 wins. What we did is something that we have seen nowhere else and makes things amazingly less complex. As in, a child with rudimentary math skills might be able to get it. Each position we label as a to i like this:

a b c

d e f

g h iWe then assigned a matrix of win contributions to each position. I changed this to using a row of 8 elements. So, in my example, we can do:

[[D1,H1,H2,H3,V1,V2,V3,D2]] where D1 is the main diagonal, H1~H3 or horizontal wins, V1~V3 are vertical wins, and D2 is the other diagonal. If a position corresponds to a win, give it a 1, like so:[a]=[[1,1,0,0,1,0,0,0]]

[b]=[[0,1,0,0,0,1,0,0]]

…

Et cetera. Now, reform this into a giant 9x8 matrix. This matrix remains constant. Here is where the actual algorithm come in :D Get ready…Now, the game matrix starts clean, with 0s: [[0,0,0,0,0,0,0,0]]. These are the wins. Now, say player one selects position [a]. Add its matrix to the game matrix and you get [[1,1,0,0,1,0,0,0]]. Player two will subtract from the game matrix, so it tries to:

1) Make a -3

2) Make as many -2s as possible without leaving any 2s. If you leave 2 -2s, this will make a trap for next turn. If you leave a 2, then X will win next turn.

3) Make as many 2s into 1s if you cannot do any of that. Remember, a 2 now will turn into a 3 the next move and 3 means X got 3 in a row!See how beautiful that is? For X, it follows the same algorithm, but use the negative of any of the numbers :) The really nice part is that:

Now, I would post my tic-tac-toe program, but I apparently never saved my final version (which was in english instead of french and had the bugs fixed). Instead, I will show you a screenie :)

-You can easily include random choices of moves that fit the highest criterion.

-When the game is over, use the winning matrix and any 3s or -3s are wins, so you will know exactly where to strike through for wins!

` ``P/3 If Ans≤1:19→G If Ans≤2:29→G If Ans≤3:39→G If sum(P={1,4,7 35→U If sum(P={2,5,8 46→U If sum(P={3,6,9 56→U`

to

` ``P/3→Q If Ans≤1:19 If Ans≤2:29 If Ans≤3:39 Ans→G 26+30fPart(Q Ans-(Ans=36)+30(Ans=26→U`

Not quite as nice but still a few bytes saved.

I did not notice this the first time around, but the following drawing commands could be sped up with Ans:

` ``Line(H+14,V+2,H+22,V+2 --> Line(Ans+14,V+2,Ans+22,V+2 or V+2:Line(H+14,Ans,H+22,Ans and Text(⁻1,G,U,sub("OX",T,1 --> Text(⁻1,G,Ans,sub("OX",T,1`

where the first change is either speed or space optimized, respectively.

I don't think there are any major structure optimizations to be found, so its mostly little changes to pack the whole thing down.

` ``SetUpEditor ʟM,ʟMC,ʟMP,ʟMW {30,1001,7429,238,627,1495,506,935→ʟMW {0→ʟM {0→ʟMC StoreGDB GDB1 AxesOff GridOff CoordOff Full LabelOff ExprOff RectGC Normal Func 63→Ymax:0→Ymin 93→Xmax:0→Xmin ClrDraw Text(⁻1,2,14,"M.E.N.A.C.E. Line(29,49,65,49 Line(65,49,65,13 Line(65,13,29,13 Line(29,13,29,49 Line(32,47,62,47 Line(31,46,63,46 Line(63,46,63,16 Line(62,47,62,15 Line(62,15,32,15 Line(61,15,33,15 Line(31,16,31,46 Line(32,15,32,47 Line(42,45,42,15 Line(52,45,52,15 Line(33,25,61,25 Line(33,36,61,36 19→H 35→V 1→P Repeat 10=dim(ʟM 1→T If fPart(dim(ʟM)/2:2→T If T=2:Text(⁻1,52,10,"CROSSES TURN! If T=1:Text(⁻1,52,10,"NAUGHTS TURN! If (T=2)(dim(ʟM)<10:Then {3,7,9,2,4,7,5,1,1→ʟMP If 4>dim(ʟM:{0,0,8,0,0,2,0,0,0→ʟMP Repeat not(sum(C=ʟM ʟMP 1+sum(cumSum(Ans)<randsum(Ans→C End C→P End Line(H+14,V+2,H+22,V+2 Repeat C or sum(K={45,21,105 Repeat C or sum(K={45,21,105,24,25,26,34 getKey→K End If K=45:ClrHome If K=45:Stop If C or sum(K={24,25,26,34:Then Line(H+14,V+2,H+22,V+2,0 P+(K=26)(P<9)-(K=24)(P>1)+3(K=34)(P≤6)-3(K=25)(P≥4)→P P/3→Q If Ans≤1:35 If Ans≤2:24 If Ans≤3:14 Ans→V 9+30fPart(Q Ans+30(Ans=9→H Line(H+14,V+2,H+22,V+2 If C:105→K DelVar C End End If (K=105)not(sum(P=ʟM:Then P→ʟM(1+dim(ʟM Line(H+14,V+2,H+22,V+2,0 P/3 If Ans≤1:19→G If Ans≤2:29→G If Ans≤3:39→G If sum(P={1,4,7 35→U If sum(P={2,5,8 46→U If sum(P={3,6,9 56→U Text(⁻1,G,U,sub("OX",T,1 expr(sub("020305071113171923",2P-1,2→ʟMC(dim(ʟM If 6≤dim(ʟMC:Then:1 For(E,2,dim(ʟMC),2 AnsʟMC(E End If not(sum(not(fPart(Ans/ʟMW:Then:1 For(E,3,dim(ʟMC),2 AnsʟMC(E End End sum(not(fPart(Ans/ʟMW→W If W:10→dim(ʟM AxesOff End DelVar C End End If T:Text(⁻1,52,10," " If not(W:Then Text(⁻1,52,23,"TIE GAME! Pause Else If T=2:Text(⁻1,52,15,"CROSSES WIN! If T=1:Text(⁻1,52,15,"NAUGHTS WIN! If T:Pause End RecallGDB GDB1 ClrDraw ClrHome`

` ``Repeat dim(ʟM)=10 --> Repeat 10=dim(ʟM 1→T If fPart(dim(ʟM)/2):2→T --> If fPart(.5dim(ʟM:2→T If T=2:Text(⁻1,52,10,"CROSSES TURN! If T=1:Text(⁻1,52,10,"NAUGHTS TURN! If (T=2)(dim(ʟM)<10):Then --> If T=2 and 10<dim(ʟM:Then {3,7,9,2,4,7,5,1,1}→ʟMP --> {3,7,9,2,4,7,5,1,1→ʟMP If dim(ʟM)<4:{0,0,8,0,0,2,0,0,0}→ʟMP --> If 4<dim(ʟM:{0,0,8,0,0,2,0,0,0→ʟMP`

There are of course more spots to do this, but they get less frequent later on.

The second bit I noticed was the win condition checker:

` ``If dim(ʟMC)≥6:Then 1→A For(E,2,dim(ʟMC),2 AʟMC(E)→A End If not(sum(not(fPart(A/{30,1001,7429,238,627,1495,506,935}):Then 1→A For(E,3,dim(ʟMC),2 AʟMC(E)→A End End sum(not(fPart(A/{30,1001,7429,238,627,1495,506,935})→W`

Firstly, I think you could turn A into Ans and pick up some speed:

` ``If 6≤dim(ʟMC:Then:1 For(E,2,dim(ʟMC),2 AnsʟMC(E End If not(sum(not(fPart(Ans/{30,1001,7429,238,627,1495,506,935:Then:1 For(E,3,dim(ʟMC),2 AnsʟMC(E End End sum(not(fPart(Ans/{30,1001,7429,238,627,1495,506,935→W`

Second, I'd put the list of prime products in some list like L₁ during initialization. You could probably also store `dim(ʟMC)` as a space saver:

` ``dim(ʟMC→A If 6≤Ans:Then:1 For(E,2,A,2 AnsʟMC(E End If not(sum(not(fPart(Ans/L₁:Then:1 For(E,3,A,2 AnsʟMC(E End End sum(not(fPart(Ans/L₁→W`

And for this section:

` ``P/3 If Ans≤1:35→V If Ans≤2:24→V If Ans≤3:14→V If sum(P={1,4,7 19→H If sum(P={2,5,8 29→H If sum(P={3,6,9 39→H`

you could probably get away with:

` ``P/3→Q If Ans≤1:35 If Ans≤2:24 If Ans≤3:14 Ans→V 9+30fPart(Q Ans+30(Ans=9→H`

I'll look at the whole code in-depth a bit more, but this is what I've got for now.

]]>-or-

A bit of explanation on confusing parts:

C is the actual selected square, or the **choice** the player made

P is the highlighted **position** on the gameboard

C and P should be the same 90% of the time, but I used 2 variables because thats just how I did it (deal with it)

2 | 3 | 5 |

7 | 11 | 13 |

17 | 19 | 23 |

By using the first 9 primes, each row column and diagonal will multiply together to a unique product (30, 1001, 7429, 238, 627, 1495, 506, or 935). I take the positions already played (as their corresponding primes), and take the product. If the product is divisible by any of the 8 unique prime products with no remainder, then it means that somebody has won.

]]>