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
|