Solution (source code)

= Solution

Construct each possible winning line, then test whether one is contained in the player's occupancy <set>:

``
winningLines :: [Places]
winningLines =
  [[(r,c) | c <- [0..2]] | r <- [0..2]] ++
  [[(r,c) | r <- [0..2]] | c <- [0..2]] ++
  [[(i,i) | i <- [0..2]], [(i,2-i) | i <- [0..2]]]

won :: Places -> Bool
won ps = any (\line -> all (`elem` ps) line) winningLines
``

The <list comprehension> generates three rows, three columns and two diagonals. Each has exactly three coordinates; therefore `all` holds precisely when every coordinate of that line is occupied by this player. Taking `any` over the eight lines proves the win test correct.