Loading the Kaya editor…
// Tic-Tac-Toe with a face: board.html draws the board, and this program
// owns every rule. You are X; the program answers as O by taking the first
// open square, so every game is deterministic and no model is called.
//
// faces.json declares the face: it reads ttt.state and sends ttt.move
// {cell}. Each move the program consumes is answered by exactly one
// ttt.state, so the face can lock the board while a move is in flight.
// Any session client can post ttt.move straight to the event door, past the
// face's schema, so map the raw value to a board index or -1.
function cellIndex(raw: any): number {
for k in [0, 1, 2, 3, 4, 5, 6, 7, 8] {
if raw == k {
return k
}
}
return -1
}
function firstOpen(cells: string[]): number {
for k in [0, 1, 2, 3, 4, 5, 6, 7, 8] {
if cells[k] == "" {
return k
}
}
return -1
}
function hasLine(cells: string[], mark: string): boolean {
let lines = [[0, 1, 2], [3, 4, 5], [6, 7, 8], [0, 3, 6], [1, 4, 7], [2, 5, 8], [0, 4, 8], [2, 4, 6]]
for line in lines {
if cells[line[0]] == mark && cells[line[1]] == mark && cells[line[2]] == mark {
return true
}
}
return false
}
mount(component: "tic-tac-toe")
"You are X. Click a square in the Preview and I'll answer as O."
let cells = ["", "", "", "", "", "", "", "", ""]
let over = false
let status = "Your move"
emitEvent(eventType: "ttt.state", data: { cells: cells, status: status, over: over })
while !over {
let played = awaitEvent(eventType: "ttt.move")
let i = cellIndex(raw: played.content[0].data?.cell ?? -1)
status = "Your move"
if i < 0 {
status = "That is not a square. Your move"
} else if cells[i] != "" {
status = "That square is taken. Your move"
} else {
cells[i] = "X"
if hasLine(cells: cells, mark: "X") {
status = "You win!"
over = true
} else {
let o = firstOpen(cells: cells)
if o < 0 {
status = "Draw"
over = true
} else {
cells[o] = "O"
// Bind calls first: an else-if condition cannot contain a call.
let oWins = hasLine(cells: cells, mark: "O")
let left = firstOpen(cells: cells)
if oWins {
status = "I win!"
over = true
} else if left < 0 {
status = "Draw"
over = true
}
}
}
}
emitEvent(eventType: "ttt.state", data: { cells: cells, status: status, over: over })
}
"Game over: {{status}}"