Faye, Founder of EdgeXene LLC, created Quaryn -- a new programming language. It is not yet ready for public distribution. Quaryn Intro -> · Technical introduction ->
Written entirely in Quaryn

Tic Tac Toe

A complete game in one file: board drawing, win and draw detection, move validation, alternating players. No libraries, no dependencies, about 130 lines.

The recording

The demo game, start to finish

This is the real program running, captured frame by frame. X plays the centre, then the corners, and takes the diagonal.

Animated recording of the tic tac toe demo game running in a terminal
How the demo is run

You supply the moves

Squares are numbered 0 to 8, left to right, top to bottom. List them in order, alternating X then O. Leave them off and it plays the demo above.

012345678
$ quaryn run examples/tictactoe.qry 4 1 0 2 8

Invalid squares bounce

A square that is off the board, or one already taken, says why and does not cost you your turn.

Typos are handled

Anything that is not a number is rejected as off-board rather than quietly becoming square 0.

It knows when it is over

Wins on any of the eight lines, and a full board with no winner is called a draw.

Every line of it

The full source

Note the first line. Because the game reads its moves from the command line, the file has to declare that power up front, and the compiler enforces it.

//! run
uses [env]
// Tic tac toe. Squares are numbered 0-8, left to right, top to bottom:
//
//     0 | 1 | 2
//     3 | 4 | 5
//     6 | 7 | 8
//
// Play by listing your moves, alternating X then O:
//   quaryn run examples/tictactoe.qry 4 0 8 2 0
// With no moves given, it plays a short demo game.

fn mark_of(v: Int) -> String {
    match v {
        1 => "X",
        2 => "O",
        _ => ".",
    }
}

fn render(b: &List<Int>) -> String {
    let mut out = ""
    let mut r = 0
    while r < 3 {
        let mut line = ""
        let mut c = 0
        while c < 3 {
            line = line + " " + mark_of(b[r * 3 + c])
            c = c + 1
        }
        out = out + line + "\n"
        r = r + 1
    }
    out
}

// A line pays out only if all three squares match and are not empty.
fn line_winner(b: &List<Int>, p: Int, q: Int, r: Int) -> Int {
    let first = b[p]
    if first != 0 && first == b[q] && first == b[r] {
        first
    } else {
        0
    }
}

fn winner(b: &List<Int>) -> Int {
    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]
    let mut i = 0
    let mut found = 0
    while i < lines.len() {
        let w = line_winner(b, lines[i], lines[i + 1], lines[i + 2])
        if w != 0 && found == 0 {
            found = w
        }
        i = i + 3
    }
    found
}

fn is_full(b: &List<Int>) -> Bool {
    let mut i = 0
    let mut empty = 0
    while i < b.len() {
        if b[i] == 0 {
            empty = empty + 1
        }
        i = i + 1
    }
    empty == 0
}

// Applies one move. Answers whether it was accepted, so a rejected move
// does not cost that player their turn.
fn play(b: &mut List<Int>, square: Int, player: Int) -> Bool {
    if square < 0 || square > 8 {
        println(f"square {square} is off the board, ignored")
        return false
    }
    if b[square] != 0 {
        println(f"square {square} is already taken, ignored")
        return false
    }
    b[square] = player
    println(f"{mark_of(player)} takes {square}")
    true
}

fn main() {
    let mut board: List<Int> = [0, 0, 0, 0, 0, 0, 0, 0, 0]

    // Moves come from the command line; the demo runs when none are given.
    let given = args()
    let mut moves: List<Int> = []
    let mut i = 0
    while i < given.len() {
        moves.push(given[i].to_int().unwrap_or(-1))
        i = i + 1
    }
    if moves.len() == 0 {
        moves = [4, 1, 0, 2, 8]
        println("No moves given, playing the demo game.")
    }

    println("Squares are numbered 0-8:")
    println(" 0 1 2\n 3 4 5\n 6 7 8\n")

    let mut turn = 0
    let mut player = 1
    let mut done = 0
    while turn < moves.len() {
        if done == 0 {
            if play(&mut board, moves[turn], player) {
                println(render(&board))
                let w = winner(&board)
                if w != 0 {
                    println(f"{mark_of(w)} wins.")
                    done = 1
                } else {
                    if is_full(&board) {
                        println("A draw.")
                        done = 1
                    }
                }
                player = if player == 1 { 2 } else { 1 }
            }
        }
        turn = turn + 1
    }

    if done == 0 {
        println("Game unfinished. Add more moves to keep playing.")
    }
}