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]
}
-------------------------------------------
fact initial {
no first.places
}
fact traces {
all b: Board - last |
some r,c: Index, p: Player |
move[b, r, c, p, b.next]
}
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, 5 int