Lightweight TicTacToe in F#
let emptyGrid = (None, Array2D.init 3 3 (fun _ _ -&gt; Mark.None))



This can be simplified using Array2D.create:

let emptyGrid = None, Array2D.create 3 3 Mark.None




In rotateCw and placeMark, x is used as the first index and y as the second. This is confusing, as the usual order is row then column. For instance,

let test = Array2D.create 3 3 Mark.None
test.[2, 1] &lt;- Cross
printfn "%A" test

[[None; None; None]
 [None; None; None]
 [None; Cross; None]]




rotateCw can be simplified (assumes the indexing changes in the previous paragraph)

let rotateCw (grid : 'a[,]) = Array2D.init 3 3 (fun y x -&gt; grid.[2 - x, y])




I find this version of placeMark a bit more readable

let placeMark (x, y) mark (_, grid : Mark[,]) =
    let res = Array2D.copy grid
    res.[y, x] &lt;- mark
    (checkOutcome res, res)