"What is your name? " ask -> name;
"Hello, " print
name print
"!" say
play_game
////////////////////////////////////////////////////////////////////////////////
// Types
////////////////////////////////////////////////////////////////////////////////
type Board:
    case board_of_cells (List<Cell>)
type Cell:
    case vacant
    case occupied (Player)
type Player:
    case player_x
    case player_o
type Move:
    case mark (Int32, Int32)
    case forfeit
type Outcome:
    case continue_playing
    case tied
    case wins (Player)
////////////////////////////////////////////////////////////////////////////////
// Gameplay
////////////////////////////////////////////////////////////////////////////////
define play_game (-> +IO +Exit):
    do (while) { play_round maybe_play_again }
define play_round (-> +IO +Exit):
    new_game take_turns announce_winner print
define maybe_play_again (-> Bool +IO):
    "Play another game? [yN] " ask -> response;
    if (response is_yes):
        true
    elif (response empty | response is_no):
        false
    else:
        maybe_play_again
define take_turns (Board, Player -> Board, Optional<Player> +IO +Exit):
    -> current_player;
    match (dup outcome)
    case continue_playing:
        current_player take_turn
        current_player end_turn
        take_turns
    case wins: some
    case tied: none
define take_turn (Board, Player -> Board +IO +Exit):
    -> board, current_player;
    board print
    current_player announce_turn
    match (enter_move)
    case some:
        match
        case mark -> x, y:
            board x y current_player mark_board
            match
            case some {}
            case none:
                "Not a valid move." say
                board current_player take_turn
        case forfeit:
            0 exit
    case none:
        "I didn't understand that. Type, e.g., \"a3\" to \
        move in the bottom-left cell. Type \"q\" to exit." say
        board current_player take_turn
define end_turn (Player -> Player):
    match case player_x { player_o } case player_o { player_x }
define enter_move (-> Optional<Move> +IO):
    "Enter your move: " ask parse_move
define announce_turn (Player -> +IO):
    show ", it's your turn." cat say
define announce_winner (Optional<Player> -> +IO):
    match case none { "It's a tie!" } case some { show " wins!" cat } say
////////////////////////////////////////////////////////////////////////////////
// Board Operations
////////////////////////////////////////////////////////////////////////////////
define outcome (Board -> Outcome):
    -> board;
    possible_wins { board possible_winner } map concat_optionals -> winners;
    if (winners empty):
        continue_playing
    elif (winners \(= player_x) all):
        player_x wins
    elif (winners \(= player_o) all):
        player_o wins
    else:
        tied
define possible_winner (List<Int32>, Board -> Optional<Player>):
    board_cells swap get_all concat_optionals -> players;
    if (players \(= player_x occupied) all):
        player_x some
    elif (players \(= player_o occupied) all):
        player_o some
    else:
        none
define possible_wins (-> List<List<Int32>>):
    [
        [0, 1, 2], [3, 4, 5], [6, 7, 8],  // Rows
        [0, 3, 6], [1, 4, 7], [2, 5, 8],  // Columns
        [0, 4, 8], [2, 4, 6],             // Diagonals
    ]
define mark_board (Board, Int32, Int32, Player -> Optional<Board>):
    -> current_player;
    cell_number -> number;
    number board_index -> index;
    board_cells
    do (with (+Fail)):
        match (dup index get "invalid index" from_some)
        case vacant:
            (current_player occupied) index set
            "invalid index" from_some board_of_cells some
        case occupied:
            drop2 none
////////////////////////////////////////////////////////////////////////////////
// Input
////////////////////////////////////////////////////////////////////////////////
define parse_move (List<Char> -> Optional<Move>):
    -> chars;
    do (with (+Fail)):
        if (chars length = 2):
            (chars 0 get "invalid move" from_some)
            (chars 1 get "invalid move" from_some)
            read_x_y
        // HACK: Needs generic instances.
        elif (chars length = 1 && { chars 0 get "invalid move" from_some = 'q' }):
            forfeit some
        else:
            none
define read_x_y (Char, Char -> Optional<Move>):
    match (dup2 try_read_x_y)
    case some: \drop2 dip some
    case none: swap try_read_x_y
define try_read_x_y (Char, Char -> Optional<Move>):
    \read_x \read_y both \mark lift_optional_2
define read_x (Char -> Optional<Int32>):
    -> c;
    if   (c "aA" elem): 0 some
    elif (c "bB" elem): 1 some
    elif (c "cC" elem): 2 some
    else:                 none
define read_y (Char -> Optional<Int32>):
    -> c;
    if   (c "1!" elem): 0 some
    elif (c "2@" elem): 1 some
    elif (c "3#" elem): 2 some
    else:                 none
////////////////////////////////////////////////////////////////////////////////
// Output
////////////////////////////////////////////////////////////////////////////////
instance show (Board -> List<Char> +Fail):
    -> board;
    "  --+-+--\n" -> divider;
    "   A B C\n" -> heading;
    [
        heading, "1 ", board 0 show_row, "\n",
        divider, "2 ", board 1 show_row, "\n",
        divider, "3 ", board 2 show_row, "\n",
    ] concat
define show_row (Board, Int32 -> List<Char> +Fail):
    (* 3) -> row_index;
    -> board;
    [
        " ", board (0 + row_index) show_cell,
        "|", board (1 + row_index) show_cell,
        "|", board (2 + row_index) show_cell,
    ] concat
define show_cell (Board, Int32 -> List<Char> +Fail):
    \board_cells dip board_index get
    "invalid board index" from_some show
instance show (Player -> List<Char>):
    match
    case player_x: "X"
    case player_o: "O"
instance show (Cell -> List<Char>):
    match
    case vacant:   " "
    case occupied: show
////////////////////////////////////////////////////////////////////////////////
// Constants and Helpers
////////////////////////////////////////////////////////////////////////////////
define new_game (-> Board, Player):
    empty_board first_player
define empty_board (-> Board):
    vacant 9 replicate board_of_cells
define first_player (-> Player):
    player_x
define is_yes (List<Char> -> Bool):
    { "yY" elem } any
define is_no (List<Char> -> Bool):
    { "nN" elem } any
define board_cells (Board -> List<Cell>):
  match case board_of_cells {}
// Converts an (x, y) position to a cell number.
define cell_number (Int32, Int32 -> Int32):
    -> x, y; 3 * y + x
// Converts a cell number to a board index.
define board_index (Int32 -> Int32):
    (8 -)
////////////////////////////////////////////////////////////////////////////////
// Hacks and Workarounds
////////////////////////////////////////////////////////////////////////////////
// HACK: Should be derived automatically.
instance = (Player, Player -> Bool):
  match
  case player_x: match case player_x { true } case player_o { false }
  case player_o: match case player_x { false } case player_o { true }
// HACK: Should be derived automatically.
instance = (Cell, Cell -> Bool):
  -> cx, cy;
  match (cx)
  case occupied -> x: match (cy) case occupied -> y { x = y } else { false }
  case vacant: match (cy) case vacant { true } else { false }
https://github.com/evincarofautumn/kitten
 A statically typed concatenative systems programming language. Kitten is a statically typed, stack-based functional programming language designed for simplicity, speed, and safety. This is an in-progress implementation of that language, including: An interactive console for testing code; An interpreter; A native-code compiler producing static executables (incomplete).
Last modified 31 October 2025