aboutsummaryrefslogtreecommitdiffstats
path: root/test/tictactoe/tictactoe_board.lua
diff options
context:
space:
mode:
Diffstat (limited to 'test/tictactoe/tictactoe_board.lua')
-rw-r--r--test/tictactoe/tictactoe_board.lua155
1 files changed, 155 insertions, 0 deletions
diff --git a/test/tictactoe/tictactoe_board.lua b/test/tictactoe/tictactoe_board.lua
new file mode 100644
index 0000000..8962257
--- /dev/null
+++ b/test/tictactoe/tictactoe_board.lua
@@ -0,0 +1,155 @@
+local error = error
+local insert = table.insert
+local ipairs = ipairs
+local setmetatable = setmetatable
+local type = type
+local unpack = unpack
+
+require 'curses'
+local addstr = curses.addstr
+local addch = curses.addch
+local clrtoeol = curses.clrtoeol
+local move = curses.move
+
+module 'tictactoe_board'
+
+-- constants
+local board_background = {
+ " | | ",
+ " | | ",
+ " | | ",
+ "---+---+---",
+ " | | ",
+ " | | ",
+ " | | ",
+ "---+---+---",
+ " | | ",
+ " | | ",
+ " | | ",
+}
+
+-- constructor arguments are the coordinates on the screen where the board
+-- should be drawn
+function new(y, x)
+ -- the board uses empty tables to represent 'empty tile', since the
+ -- table constructor is guaranteed to produce unique tables, which will
+ -- compare not equal with each other
+ local board = {
+ { {}, {}, {} },
+ { {}, {}, {} },
+ { {}, {}, {} },
+ }
+ return setmetatable({board = board, yorig = y, xorig = x}, {__index = _M})
+end
+
+function clone(self)
+ local copy = new(self.yorig, self.xorig)
+ for row in ipairs(self.board) do
+ for col in ipairs(self.board[row]) do
+ if self:at(row, col) ~= nil then
+ copy.board[row][col] = self.board[row][col]
+ end
+ end
+ end
+ return copy
+end
+
+function size()
+ return #board_background, board_background[1]:len()
+end
+
+-- translate board positions to positions on the screen
+-- return a table with named members, to pass directly to the curses functions
+function position(self, row, col)
+ return {y = self.yorig + (row - 1) * 4 + 1,
+ x = self.xorig + (col - 1) * 4 + 1}
+end
+
+function draw(self)
+ -- draw the board
+ for i, line in ipairs(board_background) do
+ addstr({y = self.yorig + i - 1, x = self.xorig}, line)
+ end
+
+ -- draw the x's and o's
+ for row in ipairs(self.board) do
+ for col, tile in ipairs(self.board[row]) do
+ if tile == "x" then
+ addch(self:position(row, col), "X", {color = "red"})
+ elseif tile == "o" then
+ addch(self:position(row, col), "O", {color = "blue"})
+ end
+ end
+ end
+end
+
+function at(self, y, x)
+ if type(self.board[y][x]) == "table" then
+ return nil
+ else
+ return self.board[y][x]
+ end
+end
+
+-- return a list of empty tiles on the board, where tiles are 2 element lists
+-- of {y, x}
+function empty_tiles(self)
+ local ret = {}
+ for row in ipairs(self.board) do
+ for col in ipairs(self.board[row]) do
+ if self:at(row, col) == nil then
+ insert(ret, {row, col})
+ end
+ end
+ end
+ return ret
+end
+
+function mark(self, turn, row, col)
+ if turn == "x" or turn == "o" then
+ self.board[row][col] = turn
+ else
+ error("mark called with \'" .. turn .. "\'")
+ end
+end
+
+-- check whether there is a winner on the board
+-- returns the symbol for the winner (or nil if there is no winner), as well as
+-- a list of the tiles that make up the win, so that we can mark them later
+function winner(self)
+ -- check rows and columns
+ for i = 1, 3 do
+ if self.board[i][1] == self.board[i][2] and
+ self.board[i][2] == self.board[i][3] then
+ return self.board[i][i], {{i, 1}, {i, 2}, {i, 3}}
+ elseif self.board[1][i] == self.board[2][i] and
+ self.board[2][i] == self.board[3][i] then
+ return self.board[i][i], {{1, i}, {2, i}, {3, i}}
+ end
+ end
+
+ -- check diagonals
+ if self.board[1][1] == self.board[2][2] and
+ self.board[2][2] == self.board[3][3] then
+ return self.board[2][2], {{1, 1}, {2, 2}, {3, 3}}
+ elseif self.board[3][1] == self.board[2][2] and
+ self.board[2][2] == self.board[1][3] then
+ return self.board[2][2], {{3, 1}, {2, 2}, {1, 3}}
+ end
+end
+
+function mark_winner(self, winner, winner_tiles)
+ for _, loc in ipairs(winner_tiles) do
+ addch(self:position(unpack(loc)), winner:upper(), {color = "green"})
+ end
+end
+
+-- draw a string centered under the board
+function caption(self, str)
+ move(self.yorig + #board_background + 1, 0)
+ clrtoeol()
+ addstr({y = self.yorig + #board_background + 1,
+ x = self.xorig + (board_background[1]:len() - 1) / 2 -
+ str:len() / 2},
+ str)
+end