open util/ordering[Board]
-- ^ implicit exact bound on Board

abstract sig Player {}
one sig X extends Player {}
one sig O extends Player {}

abstract sig Index {}
one sig One extends Index {}
one sig Two extends Index {}
one sig Three extends Index {}

sig Board {
  places: (Index -> Index) -> Player
}

fact atMostOnePerSquare {
  all b: Board | {
    all r, c: Index | {
	  lone b.places[r][c]
    }
  }
}

run notenough {#places = 9} for 3 int 
// give 2^5 ints to count with (32)
run enough {#places = 9} for 5 int

pred xturn[b: Board] {
  #{r, c: Index | b.places[r][c] = X} =
  #{r, c: Index | b.places[r][c] = O}
}

pred oturn[b: Board] {
 // not xturn[b]
  sub[#{r, c: Index | b.places[r][c] = X}, 1] =
  #{r, c: Index | b.places[r][c] = O}

}

run xturn for 5 int
run oturn for 5 int

pred winning[b: Board, p: Player] {
  winH[b, p] or
  winV[b, p] or
  winD[b, p]
}

pred winH[b: Board, p: Player] {
//  b.places[One][One] = p and b.places[One][Two] = p and b.places[One][Three] = p
  some r: Index | {
    b.places[r][One] = p 
    b.places[r][Two] = p 
    b.places[r][Three] = p
  }
}

pred winV[b: Board, p: Player] {
  some c: Index | {
    b.places[One][c] = p 
    b.places[Two][c] = p 
    b.places[Three][c] = p
  }
}

pred winD[b: Board, p: Player] {
  (b.places[One][One] = p and
  b.places[Two][Two] = p and
  b.places[Three][Three] = p)
  or
  (b.places[One][Three] = p and
  b.places[Two][Two] = p and
  b.places[Three][One] = p) 
}

pred valid[b: Board] {
  xturn[b] || oturn[b]
}

run winningvalid { 
  some b: Board | 
    some p: Player | 
      winning[b, p] and valid[b] 
} for 5 int

/*
pred move[b: Board, r,c: Index, p: Player, b': Board] {
   no b.places[r][c]
   b'.places = b.places + (r -> c -> p)
   p = X implies xturn[b]
   p = O implies oturn[b]
}*/

abstract sig Event {
  pre, post: Board
}
sig Move extends Event {
  r,c: Index,
  p: Player
} {
   no pre.places[r][c]
   post.places = pre.places + (r -> c -> p)
   p = X implies xturn[pre]
   p = O implies oturn[pre]
}


-------------------------------------------

fact initial {
  no first.places
}

fact traces {
 /* all b: Board - last | 
    some r,c: Index, p: Player |
      move[b, r, c, p, b.next]*/
  all b: Board - last | {
    some e: Event | e.pre = b and e.post = b.next
    --one e: Event | ...
  }
}

run {#Board = 3} for 10 Board, 5 int

check conjecture {
-- x first move is in middle
  (first.next.places = (Two -> Two -> X)) 
  implies winning[last, X]
} for 10 Board, 5 int
run vacuous_test {
  (first.next.places = (Two -> Two -> X)) 
} for 10 Board, 5 int


check conjecture_tie {
-- x first move is in middle
  (first.next.places = (Two -> Two -> X)) 
  implies (not winning[last, O] and winning[last, X])
} for 10 Board, 9 Event, 5 int

-------------------
-- sanity checking

-- check for an underconstraint
run uc1 {
  some b: Board | {
    /*#{r,c: Index | b.places[r][c] = O} = 5
    #{r,c: Index | b.places[r][c] = X} = 3*/
    not oturn[b] and not xturn[b]
  }
} for 10 Board, 9 Event, 5 int