{
  "id": "P009",
  "slug": "tic-tac-toe",
  "key": "P009-tic-tac-toe",
  "title": "Tic-tac-toe",
  "summary": "Play tic-tac-toe against a friend or against the computer, which wins when it can and blocks when it must; wins and draws are found and a running score is kept - from a text menu.",
  "entry": "main.eml",
  "ui": "terminal",
  "readme": "# P009 - Tic-tac-toe\n\nTic-tac-toe for two players, or for one player (X) against the computer (O).\nX always starts; the board shows the free squares by number; a game ends in a\nwin or a draw, or when a player leaves it with `q`. The score is kept while\nthe program runs.\n\n- `main.eml` - the menu, one game, and the checks on each square typed\n- `board.eml` - the board as nine cells, how it is drawn, the eight winning\n  lines, and whether the board is full\n- `ai.eml` - the computer's move, in order of preference: win now if it can;\n  otherwise block the square where the other player would win; otherwise the\n  centre, then the first free corner, then the first free side. There is no\n  randomness, so the same moves always get the same answer.\n\nWhat is checked: a square must be one of 1 to 9 and still free; anything else\nasks again without losing the turn.\n\nSessions: `sessions/basic.in` plays a two-player game won by X; a game against\nthe computer in which it takes the centre and a corner and blocks one of two\nthreats, and X wins with the other; a game the computer wins because winning\ncomes before blocking; a two-player draw; and the score;\n`sessions/bad-input.in` types an unknown choice, squares 0, 10, a letter and\nnothing, a taken square, and leaves a game, which does not count.\n\nBuilt on the verified corpus case `mvp-tic-tac-toe` (a board, moves that\nrefuse a taken square, and finding a line of three).\n",
  "modules": [
    {
      "name": "main.eml",
      "eml": "# P009 tic-tac-toe: two players, or you (X) against the computer (O). X\n# always starts. The score is kept while the program runs.\nimport board\nimport ai\n\ndef trim(s):\n    0 => i\n    len(s) => j\n    while i < j and s[i] == \" \":\n        i + 1 => i\n    while j > i and s[j - 1] == \" \":\n        j - 1 => j\n    return s[i:j]\n\ndef ask_square(b, who):\n    # The cell index of a free square for who, or -1 when q leaves the game.\n    while True:\n        trim(input(who + \" to play, square 1-9 (q to leave)> \")) => typed\n        if typed == \"q\":\n            return -1\n        if len(typed) == 1 and typed in \"123456789\":\n            int(typed) - 1 => i\n            if b[i] == \"\":\n                return i\n            \"That square is taken.\" ^0\n        else:\n            \"Pick a free square from 1 to 9.\" ^0\n\ndef play(vs_computer):\n    # One game. Returns \"X\", \"O\", \"draw\", or \"\" when it was left.\n    board.empty() => b\n    \"X\" => who\n    while True:\n        if vs_computer and who == \"O\":\n            ai.move(b, \"O\", \"X\") => i\n            (\"Computer plays \" + str(i + 1) + \".\") ^0\n        else:\n            board.show(b)\n            ask_square(b, who) => i\n            if i < 0:\n                \"Game left.\" ^0\n                return \"\"\n        board.placed(b, i, who) => b\n        board.winner(b) => w\n        if w != \"\":\n            board.show(b)\n            if not vs_computer:\n                (w + \" wins.\") ^0\n            elif w == \"X\":\n                \"You win.\" ^0\n            else:\n                \"The computer wins.\" ^0\n            return w\n        if board.full(b):\n            board.show(b)\n            \"Draw.\" ^0\n            return \"draw\"\n        if who == \"X\":\n            \"O\" => who\n        else:\n            \"X\" => who\n\n[0, 0, 0] => score\nTrue => running\nwhile running:\n    \"\" ^0\n    \"== Tic-tac-toe ==\" ^0\n    \"1) two players  2) against the computer  3) score  4) quit\" ^0\n    trim(input(\"choice> \")) => choice\n    if choice == \"1\" or choice == \"2\":\n        play(choice == \"2\") => result\n        if result == \"X\":\n            score[0] + 1 => score[0]\n        elif result == \"O\":\n            score[1] + 1 => score[1]\n        elif result == \"draw\":\n            score[2] + 1 => score[2]\n    elif choice == \"3\":\n        (\"X wins: \" + str(score[0]) + \", O wins: \" + str(score[1]) + \", draws: \" + str(score[2])) ^0\n    elif choice == \"4\":\n        False => running\n    else:\n        \"Pick a number from 1 to 4.\" ^0\n\"Bye.\" ^0\n",
      "python": "import board\nimport ai\n\ndef trim(s):\n    i = 0\n    j = len(s)\n    while i < j and s[i] == \" \":\n        i = i + 1\n    while j > i and s[j - 1] == \" \":\n        j = j - 1\n    return s[i:j]\n\ndef ask_square(b, who):\n    while True:\n        typed = trim(input(who + \" to play, square 1-9 (q to leave)> \"))\n        if typed == \"q\":\n            return -1\n        if len(typed) == 1 and typed in \"123456789\":\n            i = int(typed) - 1\n            if b[i] == \"\":\n                return i\n            print(\"That square is taken.\")\n        else:\n            print(\"Pick a free square from 1 to 9.\")\n\ndef play(vs_computer):\n    b = board.empty()\n    who = \"X\"\n    while True:\n        if vs_computer and who == \"O\":\n            i = ai.move(b, \"O\", \"X\")\n            print(\"Computer plays \" + str(i + 1) + \".\")\n        else:\n            board.show(b)\n            i = ask_square(b, who)\n            if i < 0:\n                print(\"Game left.\")\n                return \"\"\n        b = board.placed(b, i, who)\n        w = board.winner(b)\n        if w != \"\":\n            board.show(b)\n            if not vs_computer:\n                print(w + \" wins.\")\n            elif w == \"X\":\n                print(\"You win.\")\n            else:\n                print(\"The computer wins.\")\n            return w\n        if board.full(b):\n            board.show(b)\n            print(\"Draw.\")\n            return \"draw\"\n        if who == \"X\":\n            who = \"O\"\n        else:\n            who = \"X\"\n\nscore = [0, 0, 0]\nrunning = True\nwhile running:\n    print(\"\")\n    print(\"== Tic-tac-toe ==\")\n    print(\"1) two players  2) against the computer  3) score  4) quit\")\n    choice = trim(input(\"choice> \"))\n    if choice == \"1\" or choice == \"2\":\n        result = play(choice == \"2\")\n        if result == \"X\":\n            score[0] = score[0] + 1\n        elif result == \"O\":\n            score[1] = score[1] + 1\n        elif result == \"draw\":\n            score[2] = score[2] + 1\n    elif choice == \"3\":\n        print(\"X wins: \" + str(score[0]) + \", O wins: \" + str(score[1]) + \", draws: \" + str(score[2]))\n    elif choice == \"4\":\n        running = False\n    else:\n        print(\"Pick a number from 1 to 4.\")\nprint(\"Bye.\")\n"
    },
    {
      "name": "board.eml",
      "eml": "# P009 tic-tac-toe - the board. It is a list of nine cells, index 0 to 8 for\n# squares 1 to 9 in reading order; a cell is \"X\", \"O\" or \"\" when empty.\n\n# The eight lines that win: three rows, three columns, two diagonals.\n[\n    [0, 1, 2], [3, 4, 5], [6, 7, 8],\n    [0, 3, 6], [1, 4, 7], [2, 5, 8],\n    [0, 4, 8], [2, 4, 6],\n] => lines\n\ndef empty():\n    return [\"\" for i in [0:8]]\n\ndef mark(cell, i):\n    # What a square shows: its mark, or its number while it is free.\n    if cell == \"\":\n        return str(i + 1)\n    return cell\n\ndef show(b):\n    \"\" ^0\n    for r in [0:2]:\n        (\" \" + mark(b[3 * r], 3 * r) + \" | \" + mark(b[3 * r + 1], 3 * r + 1) + \" | \" + mark(b[3 * r + 2], 3 * r + 2)) ^0\n        if r < 2:\n            \"---+---+---\" ^0\n\ndef winner(b):\n    # \"X\" or \"O\" if that player has a whole line, otherwise \"\".\n    for line in lines:\n        b[line[0]] => a\n        if a != \"\" and b[line[1]] == a and b[line[2]] == a:\n            return a\n    return \"\"\n\ndef full(b):\n    for cell in b:\n        if cell == \"\":\n            return False\n    return True\n\ndef placed(b, i, who):\n    # A new board with who's mark on cell i.\n    return b[0:i] + [who] + b[i + 1:9]\n",
      "python": "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]]\n\ndef empty():\n    return [\"\" for i in range(0, 9)]\n\ndef mark(cell, i):\n    if cell == \"\":\n        return str(i + 1)\n    return cell\n\ndef show(b):\n    print(\"\")\n    for r in range(0, 3):\n        print(\" \" + mark(b[3 * r], 3 * r) + \" | \" + mark(b[3 * r + 1], 3 * r + 1) + \" | \" + mark(b[3 * r + 2], 3 * r + 2))\n        if r < 2:\n            print(\"---+---+---\")\n\ndef winner(b):\n    for line in lines:\n        a = b[line[0]]\n        if a != \"\" and b[line[1]] == a and b[line[2]] == a:\n            return a\n    return \"\"\n\ndef full(b):\n    for cell in b:\n        if cell == \"\":\n            return False\n    return True\n\ndef placed(b, i, who):\n    return b[0:i] + [who] + b[i + 1:9]\n"
    },
    {
      "name": "ai.eml",
      "eml": "# P009 tic-tac-toe - the computer's move, in order of preference: win now if\n# it can; otherwise block the square where the other player would win;\n# otherwise take the centre, then the first free corner, then the first free\n# side. No randomness, so the same moves always get the same answer.\nimport board\n\ndef completing(b, who):\n    # The free cell that would give who a whole line, or -1. Lines are tried\n    # in order, so the first such cell is the answer.\n    for line in board.lines:\n        0 => mine\n        -1 => free\n        for i in line:\n            if b[i] == who:\n                mine + 1 => mine\n            elif b[i] == \"\":\n                i => free\n        if mine == 2 and free >= 0:\n            return free\n    return -1\n\ndef move(b, me, other):\n    completing(b, me) => i\n    if i >= 0:\n        return i\n    completing(b, other) => i\n    if i >= 0:\n        return i\n    for i in [4, 0, 2, 6, 8, 1, 3, 5, 7]:\n        if b[i] == \"\":\n            return i\n    return -1\n",
      "python": "import board\n\ndef completing(b, who):\n    for line in board.lines:\n        mine = 0\n        free = -1\n        for i in line:\n            if b[i] == who:\n                mine = mine + 1\n            elif b[i] == \"\":\n                free = i\n        if mine == 2 and free >= 0:\n            return free\n    return -1\n\ndef move(b, me, other):\n    i = completing(b, me)\n    if i >= 0:\n        return i\n    i = completing(b, other)\n    if i >= 0:\n        return i\n    for i in [4, 0, 2, 6, 8, 1, 3, 5, 7]:\n        if b[i] == \"\":\n            return i\n    return -1\n"
    }
  ],
  "sessions": [
    {
      "name": "bad-input",
      "input": "9\n3\n1\n0\n10\nx\n\n5\n5\nq\n3\n4\n",
      "screen": "\n== Tic-tac-toe ==\n1) two players  2) against the computer  3) score  4) quit\nchoice> 9\nPick a number from 1 to 4.\n\n== Tic-tac-toe ==\n1) two players  2) against the computer  3) score  4) quit\nchoice> 3\nX wins: 0, O wins: 0, draws: 0\n\n== Tic-tac-toe ==\n1) two players  2) against the computer  3) score  4) quit\nchoice> 1\n\n 1 | 2 | 3\n---+---+---\n 4 | 5 | 6\n---+---+---\n 7 | 8 | 9\nX to play, square 1-9 (q to leave)> 0\nPick a free square from 1 to 9.\nX to play, square 1-9 (q to leave)> 10\nPick a free square from 1 to 9.\nX to play, square 1-9 (q to leave)> x\nPick a free square from 1 to 9.\nX to play, square 1-9 (q to leave)> \nPick a free square from 1 to 9.\nX to play, square 1-9 (q to leave)> 5\n\n 1 | 2 | 3\n---+---+---\n 4 | X | 6\n---+---+---\n 7 | 8 | 9\nO to play, square 1-9 (q to leave)> 5\nThat square is taken.\nO to play, square 1-9 (q to leave)> q\nGame left.\n\n== Tic-tac-toe ==\n1) two players  2) against the computer  3) score  4) quit\nchoice> 3\nX wins: 0, O wins: 0, draws: 0\n\n== Tic-tac-toe ==\n1) two players  2) against the computer  3) score  4) quit\nchoice> 4\nBye.\n",
      "interpreter": "equal"
    },
    {
      "name": "basic",
      "input": "1\n1\n4\n2\n5\n3\n2\n1\n9\n7\n4\n2\n2\n8\n3\n1\n1\n5\n9\n2\n8\n7\n3\n6\n4\n3\n4\n",
      "screen": "\n== Tic-tac-toe ==\n1) two players  2) against the computer  3) score  4) quit\nchoice> 1\n\n 1 | 2 | 3\n---+---+---\n 4 | 5 | 6\n---+---+---\n 7 | 8 | 9\nX to play, square 1-9 (q to leave)> 1\n\n X | 2 | 3\n---+---+---\n 4 | 5 | 6\n---+---+---\n 7 | 8 | 9\nO to play, square 1-9 (q to leave)> 4\n\n X | 2 | 3\n---+---+---\n O | 5 | 6\n---+---+---\n 7 | 8 | 9\nX to play, square 1-9 (q to leave)> 2\n\n X | X | 3\n---+---+---\n O | 5 | 6\n---+---+---\n 7 | 8 | 9\nO to play, square 1-9 (q to leave)> 5\n\n X | X | 3\n---+---+---\n O | O | 6\n---+---+---\n 7 | 8 | 9\nX to play, square 1-9 (q to leave)> 3\n\n X | X | X\n---+---+---\n O | O | 6\n---+---+---\n 7 | 8 | 9\nX wins.\n\n== Tic-tac-toe ==\n1) two players  2) against the computer  3) score  4) quit\nchoice> 2\n\n 1 | 2 | 3\n---+---+---\n 4 | 5 | 6\n---+---+---\n 7 | 8 | 9\nX to play, square 1-9 (q to leave)> 1\nComputer plays 5.\n\n X | 2 | 3\n---+---+---\n 4 | O | 6\n---+---+---\n 7 | 8 | 9\nX to play, square 1-9 (q to leave)> 9\nComputer plays 3.\n\n X | 2 | O\n---+---+---\n 4 | O | 6\n---+---+---\n 7 | 8 | X\nX to play, square 1-9 (q to leave)> 7\nComputer plays 8.\n\n X | 2 | O\n---+---+---\n 4 | O | 6\n---+---+---\n X | O | X\nX to play, square 1-9 (q to leave)> 4\n\n X | 2 | O\n---+---+---\n X | O | 6\n---+---+---\n X | O | X\nYou win.\n\n== Tic-tac-toe ==\n1) two players  2) against the computer  3) score  4) quit\nchoice> 2\n\n 1 | 2 | 3\n---+---+---\n 4 | 5 | 6\n---+---+---\n 7 | 8 | 9\nX to play, square 1-9 (q to leave)> 2\nComputer plays 5.\n\n 1 | X | 3\n---+---+---\n 4 | O | 6\n---+---+---\n 7 | 8 | 9\nX to play, square 1-9 (q to leave)> 8\nComputer plays 1.\n\n O | X | 3\n---+---+---\n 4 | O | 6\n---+---+---\n 7 | X | 9\nX to play, square 1-9 (q to leave)> 3\nComputer plays 9.\n\n O | X | X\n---+---+---\n 4 | O | 6\n---+---+---\n 7 | X | O\nThe computer wins.\n\n== Tic-tac-toe ==\n1) two players  2) against the computer  3) score  4) quit\nchoice> 1\n\n 1 | 2 | 3\n---+---+---\n 4 | 5 | 6\n---+---+---\n 7 | 8 | 9\nX to play, square 1-9 (q to leave)> 1\n\n X | 2 | 3\n---+---+---\n 4 | 5 | 6\n---+---+---\n 7 | 8 | 9\nO to play, square 1-9 (q to leave)> 5\n\n X | 2 | 3\n---+---+---\n 4 | O | 6\n---+---+---\n 7 | 8 | 9\nX to play, square 1-9 (q to leave)> 9\n\n X | 2 | 3\n---+---+---\n 4 | O | 6\n---+---+---\n 7 | 8 | X\nO to play, square 1-9 (q to leave)> 2\n\n X | O | 3\n---+---+---\n 4 | O | 6\n---+---+---\n 7 | 8 | X\nX to play, square 1-9 (q to leave)> 8\n\n X | O | 3\n---+---+---\n 4 | O | 6\n---+---+---\n 7 | X | X\nO to play, square 1-9 (q to leave)> 7\n\n X | O | 3\n---+---+---\n 4 | O | 6\n---+---+---\n O | X | X\nX to play, square 1-9 (q to leave)> 3\n\n X | O | X\n---+---+---\n 4 | O | 6\n---+---+---\n O | X | X\nO to play, square 1-9 (q to leave)> 6\n\n X | O | X\n---+---+---\n 4 | O | O\n---+---+---\n O | X | X\nX to play, square 1-9 (q to leave)> 4\n\n X | O | X\n---+---+---\n X | O | O\n---+---+---\n O | X | X\nDraw.\n\n== Tic-tac-toe ==\n1) two players  2) against the computer  3) score  4) quit\nchoice> 3\nX wins: 2, O wins: 1, draws: 1\n\n== Tic-tac-toe ==\n1) two players  2) against the computer  3) score  4) quit\nchoice> 4\nBye.\n",
      "interpreter": "equal"
    }
  ],
  "builtOn": [
    {
      "slug": "mvp-tic-tac-toe",
      "caseId": "007-mvp-tic-tac-toe",
      "title": "MVP proof: a real, recognizable program ported to EML"
    }
  ],
  "updated": "2026-09-29"
}
