Optimal Games of Tic Tac Torus
JavaScript (ES6), 266 308 317 334 341
A function returning a string. Edit Found an arithmetic solution for function M (at last!)
F=(n,x=[],M=(a,b,t=-a-b)=>(a-b)%3?a<3&b<3?3+t:a>5&b>5?21+t:12+t:9+t+a%3*3)=>
[for(a of z='012345678')for(b of z)for(c of z)for(d of z)
a-b&&c-a&&c-b&&(b-(y=M(a,c))?d==y:d-a&&d-b&&d-c)&&(
f=M(c,e=M(b,d)),g=M(a,e),g<f?[f,g]=[g,f]:0,r=a+b+c+d+e,x.push(r+f+g,r+g+f))]
&&x[n]
Very naive , it can be shortened in many ways. It simply enumerates all the possible legal values and returns what find at place n.
The M function returns the position in between two cells, that is the mandatory move to block an opposite player.
More readable
F=(n,x=[],
M=(a,b,t=-a-b)=>(a-b)%3?
a<3&b<3?
3+t // first row
:a>5&b>5?
21+t // last row
:12+t // middle row and diags
:9+t+a%3*3 // columns
)=>
[for(a of z='012345678') // enumerate the first 4 moves
for(b of z)
for(c of z)
for(d of z)
a-b&&c-a&&c-b // avoid duplicates
&&(b-(y=M(a,c))?d==y:d-a&&d-b&&d-c) // and check if d must block a-c or it's free
&&(
e=M(b,d), // forced to block b-d
f=M(c,e),g=M(a,e),g<f?[f,g]=[g,f]:0, // f and g could be in the wrong order
r=a+b+c+d+e, // start building a return string
x.push(r+f+g,r+g+f) // store all values in x
)]&&x[n] // return value at requested position
APL (Dyalog Unicode), 73 bytes
⎕⊃z/⍨(∧/{⍵[4⍪3 2⍴5 6 1 3]∊⍨∘{3⊥3|-+/3 3⊤⍵}¨⍥↓⍵[1 3⍪3 2⍴0 4 2]},≠)¨z←,⍳7⍴9
Try it online!
A full program, and takes way too much time and memory to complete for any input. Also, the code includes some 18.0 features that aren't yet available on TIO. The TIO link includes a faster version using only features up to 17.1, so that the output can be checked.
Winning or blocking position
Given two positions, extract their row and column numbers. For each dimension (row/column), if the numbers are the same, so is the result; otherwise, the result is the third number. In both cases, the result coordinates can be found with the following property:
n1 n2 res -(n1+n2)%3
0 0 0 0
0 1 2 2
0 2 1 1
1 0 2 2
1 1 1 1
1 2 0 0
2 0 1 1
2 1 0 0
2 2 2 2
The resulting code is as simple as the following.
{3⊥3|-+/3 3⊤⍵} ⍝ ⍵←two positions
3 3⊤⍵ ⍝ Convert the numbers to base 3, each number → column
+/ ⍝ Sum of each row
3|- ⍝ Negate and modulo 3
3⊥ ⍝ Convert from base 3 to integer
How the code works
z←,⍳7⍴9 ⍝ Create an array of 7-tuples of 0..8
z/⍨(...)¨z ⍝ Filter the tuples by the following condition:
∧/{...},≠ ⍝ Check that Unique Mask (≠) is all ones AND it is an optimal TTT game:
⍵[...] ... ⍵[...] ⍝ Extract certain indexes of the given tuple as matrices
⍥↓ ⍝ Downrank both sides into four 2-tuples
∘{...}¨ ⍝ For each tuple of the right side,
3⊥3|-+/3 3⊤⍵ ⍝ Compute the cell that completes a line in TTT
∊⍨ ⍝ And check that the right side appears in
⍝ the matching tuple on the left side
⎕⊃ ⍝ Take input n from stdin and pick the nth 7-tuple
-------------------
Left side idxs ≡ ↓4⍪3 2⍴5 6 1 3 ≡ (4 4)(5 6)(1 3)(5 6)
Right side idxs ≡ ↓1 3⍪3 2⍴0 4 2 ≡ (1 3)(0 4)(2 0)(4 2)
Left Right Meaning
1 3 2 0 Turns 1 and 3 (by P1) are blocked by either Turn 2 or 4
4 4 1 3 Turns 2 and 4 (by P2) are blocked by Turn 5
5 6 0 4 Turn 6 blocks one of Turns 1,5 or Turns 3,5, and
Turn 7 completes the other
5 6 4 2 ↑
APL (Dyalog Extended), 72 bytes
g←{⍵,⍨1⌽@(∊∘⍺)¨⍵}
7↑⎕⊃∧⍋¨5 6g,¨(⊂⍳3)(∪⊢,⌽¨,⊖¨,⊢∘⌽¨)⍣≡1 3g⊂3 3⍴⊃4 6 8g⊂⍳9
Try it online!
Decided to post as a separate answer, because the approach is drastically different.
Inspired from orion's comment, this one generates all optimal games by transforming a single game board in various ways.
The 3x3 toroidal board is very very permissive on its transformations; the three-in-a-rows are preserved through reflection, rotation (e.g. cycling the top row to the bottom), and shear (e.g. cycling elements of each row 0, 1, 2 units to the right respectively). With this information, I could identify four seed boards that have distinct topology (using 0 to 6 to denote the seven moves made in sequence):
P2#4 not forced
0 1 2 0 1 2
3 6 5 3 5 6
x x 4 x x 4
P2#4 forced
0 3 2 0 3 2
1 6 5 1 5 6
x x 4 x x 4
Then I tried various combinations of the transformations to find a short one that generates all 1728 boards. And I noticed that even the seed boards can be generated from one board, by swapping the positions of 1-3 (decides whether the 4th move is forced) and 5-6 (swap a block and a win).
How it works
⍝ Helper function
g←{⍵,⍨1⌽@(∊∘⍺)¨⍵}
1⌽@(∊∘⍺)¨⍵ ⍝ Cycle the positions of ⍺s in each of ⍵
⍵,⍨ ⍝ Prepend the above to ⍵
⍝ Main program
7↑⎕⊃∧⍋¨5 6g,¨(⊂⍳3)(∪⊢,⌽¨,⊖¨,⊢∘⌽¨)⍣≡1 3g⊂3 3⍴⊃4 6 8g⊂⍳9
3 3⍴⊃4 6 8g⊂⍳9 ⍝ Generate the seed board:
0 1 2
3 6 5
8 7 4
5 6g 1 3g ⍝ Generate four seed boards from the above,
⍝ by swapping the numbers 1-3 and 5-6
(⊂⍳3)(...)⍣≡ ⍝ Repeat certain transformations until all are found:
⊢∘⌽¨ ⍝ Reverse the order of columns:
a b c c b a
d e f → f e d
g h i i h g
⊖¨, ⍝ Join with vertical shear:
a b c a e i
d e f → d h c
g h i g b f
⌽¨, ⍝ Join with horizontal shear:
a b c a b c
d e f → e f d
g h i i g h
∪⊢, ⍝ Join with original and take unique results
⍋¨,¨ ⍝ Flatten each 3x3 matrix and extract where each number is
∧ ⍝ Ascending sort
⎕⊃ ⍝ Take input n and pick nth result
7↑ ⍝ Take first 7, since the last two are garbage