aboutsummaryrefslogtreecommitdiffstats
path: root/test/tictactoe/tictactoe.lua
blob: a76c72ae74813934763d8b084b31d23421242515 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
local Board = require "tictactoe_board"
local Player = require "tictactoe_player"
require "curses"
require "signal"

-- deinitialize curses on exit, or when a signal is received, so we don't leave
-- the terminal in a messed up state
local function cleanup(sig)
    curses.endwin()
    if sig then
        signal.signal(sig, "default")
        signal.raise(sig)
    end
end

local function get_player_types()
    local player1, player2
    io.write("Is player 1 a human? ");
    local response = io.read()
    if response:sub(1, 1):lower() == "y" then
        player1 = "human"
    else
        player1 = "computer"
    end

    io.write("Is player 2 a human? ");
    local response = io.read()
    if response:sub(1, 1):lower() == "y" then
        player2 = "human"
    else
        player2 = "computer"
    end

    return player1, player2
end

local function init_curses()
    curses.initscr()
    signal.signal("INT", cleanup)
    signal.signal("TERM", cleanup)
    curses.start_color()
    curses.use_default_colors()
    curses.setup_term{nl = false, cbreak = true, echo = false, keypad = true}
    for _, color in ipairs({"red", "blue", "green"}) do
        curses.init_pair(color, color)
    end
end

local function init_board(ymax, xmax)
    local ymax, xmax = curses.getmaxyx()
    local board_y, board_x = Board.size()
    -- center the board horizontally, and place the board a little above center
    -- vertically, so that the caption isn't too low
    return Board.new(math.floor(ymax - board_y) / 2 - 1,
                     math.floor(xmax - board_x) / 2)
end

local function main()
    -- initialize the game
    local player1, player2 = get_player_types()
    init_curses()
    board = init_board()
    players = { x = Player.new(player1, "x"), o = Player.new(player2, "o") }

    -- start the main loop
    local turn = "x"
    board:draw()
    curses.refresh()
    while true do
        if #board:empty_tiles() == 0 then
            board:caption("Tie!")
            break
        end

        board:mark(turn, players[turn]:get_move(board))
        board:draw()

        local winner, winner_tiles = board:winner()
        if winner then
            board:mark_winner(winner, winner_tiles)
            board:caption(winner:upper() .. " wins!")
            break
        end
        if turn == "x" then turn = "o" else turn = "x" end
    end
end

-- use pcall to catch lua errors, which we can't catch with our signal handlers
-- (so that we can clean up curses if necessary)
local success, err_msg = pcall(main)

if success then
    curses.getch()
    cleanup()
else
    cleanup()
    print(err_msg)
end