librarygames/tic-tac-toe/TicTacToe.xtl

Tic-tac-toe's rules, a library: tic-tac-toe.xtl (scripted), play.xtl (at the terminal) and the web page all use these. The board is a vector of 9 squares, numbered row by row: 0 empty, 1 X, -1 O. X moves first.

source · imports b: lib/Board.xtl

ˡl̲ines : Num a => Unit -> a

function · line 11

The eight lines (three rows, three columns, two diagonals) as an 8 by 3 table of square numbers.

ˡl̲ines ← { @ → 8 3 r̲eshape 1 2 3 4 5 6 7 8 9 1 4 7 2 5 8 3 6 9 1 5 9 3 5 7 }

ˡs̲ums : Num a => a -> a

function · line 16

Every line's sum at once: the table used as indices into the board (an 8 by 3 matrix of squares), summed along each row. 3 is a line of X, -3 a line of O.

ˡs̲ums ← { b → '+ r̲/₂ (ˡl̲ines @) s̲elect b }

ˡw̲inner : (Num a, Num b, Truthy b) => a -> b

function · line 19

1 when X has a line, -1 when O has, else 0.

ˡw̲inner ← { b →
  s ← ˡs̲ums b
  (3 m̲ember? s) − -3 m̲ember? s
}

ˡt̲urn : (Num a, Num b, Truthy b) => a -> b

function · line 25

Whose turn: X (1) when the board has as many X as O, else O (-1).

ˡt̲urn ← { b → 1 − 2 × 0 ≠ '+ r̲/ b }

ˡn̲ew : (Any a, Num b) => a -> b

function · line 28

A new board: nine empty squares.

ˡn̲ew ← { x → 9 r̲eshape 0 }

ˡl̲egal : Num a => a -> Int

function · line 31

The empty squares, by number: the legal moves.

ˡl̲egal ← { b → w̲here b = 0 }

ˡs̲tatus : (Num a, Num b) => a -> b

function · line 35

0 playing, 1 X won, 2 O won, 3 a draw: the first of the three that holds.

ˡs̲tatus ← { b →
  w ← ˡw̲inner b
  f̲irst (((w = 1) c̲at (w = -1) c̲at 0 = t̲ally ˡl̲egal b) r̲eplicate 1 2 3) c̲at 0
}

ˡm̲ove : (Num a, Truthy a) => a -> Int -> a

function · line 41

The player to move takes square k.

ˡm̲ove ← { b k → b + (ˡt̲urn b) × k = r̲ange 9 }

ˡc̲odes : Num a => a -> Int

function · line 46

Every square's code at once: O 1, X 2, an empty square 2 plus its number (so the text shows the number to type).

ˡc̲odes ← { b → 3 3 r̲eshape (b = -1) + (2 × b = 1) + (b = 0) × 2 + r̲ange 9 }

ˡv̲iew : Num a => a -> Char

function · line 49

The board as text, spaced (lib/Board).

ˡv̲iew ← { b → ᵇs̲paced (ˡc̲odes b) s̲elect "OX123456789" }

ˡp̲icture : Num a => a -> Char

function · line 52

The board as a picture X_eTaL draws (SVG text); []S_HOW shows it.

ˡp̲icture ← { b → ⎕G̲RID (ˡc̲odes b) s̲elect "OX123456789" }
Used in: ᵘy̲ou, pic

Players

ˡo̲n : Truthy a => Unit -> a

function · line 58

Which squares lie on which lines: a 9 by 8 table (square against line), from a 9 by 8 by 3 table of every square against every entry.

ˡo̲n ← { @ → '∨ r̲/₃ (r̲ange 9) '= t̲able ˡl̲ines @ }

ˡn̲ear : (Num a, Num b, Truthy b) => a -> a -> b

function · line 61

For every square, whether a line through it sums to t.

ˡn̲ear ← { b t → '∨ r̲/₂ (ˡo̲n @) × 9 8 r̲eshape t = ˡs̲ums b }
Used in: ˡa̲i

ˡa̲i : (Num a, Truthy a) => a -> Int

function · line 68

A quick player that rates every empty square at once: four features per square (it completes a line of the mover's, it blocks one of the other's, the center, a corner), a 4 by 9 matrix, weighted 1000, 100, 10 and 5 by one inner product. A line through a square with sum 2 (or -2) has two marks and one empty square, which must be that one.

ˡa̲i ← { b →
  p ← ˡt̲urn b
  f ← (b ˡn̲ear 2 × p) c̲at (b ˡn̲ear -2 × p) c̲at (5 = r̲ange 9) c̲at (r̲ange 9) m̲ember? 1 3 7 9
  r ← (b = 0) × 1 + 1000 100 10 5 '+ '× i̲nner 4 9 r̲eshape f
  f̲irst w̲here r = 'm̲ax r̲/ r
}

ˡv̲alue : (Num a, Truthy a, Num b, Truthy b) => a -> b

function · line 81

A perfect player: minimax, written as negamax. A board's value with best play (1 X wins, -1 O wins, 0 a draw) is the winner if there is one, 0 when the board is full, else the mover's best: the mover times the largest of the mover times every move's value (every legal move tried, by recursion).

ˡv̲alue ← { b →
  w ← ˡw̲inner b
  w ≠ 0 ? w
  e ← ˡl̲egal b
  0 = t̲ally e ? 0
  p ← ˡt̲urn b
  p × 'm̲ax r̲/ p × '{ ˡv̲alue b ˡm̲ove ⍵ } e̲ach e
}

ˡb̲est : (Num a, Truthy a) => a -> Int

function · line 91

Its move: the first legal square with the best value for the mover.

ˡb̲est ← { b →
  e ← ˡl̲egal b
  v ← (ˡt̲urn b) × '{ ˡv̲alue b ˡm̲ove ⍵ } e̲ach e
  f̲irst (v = 'm̲ax r̲/ v) r̲eplicate e
}