Приложение 3. Примеры обработки событий для таблиц

Пример обработки событий мыши и клавиатуры

stopped = false
t_id = nil

old_message = message
local fmt = string.format
function message(v, t)
                 t= t or 1
                 old_message(tostring(v), t)
end

function OnStop(s)
	stopped = true
	if t_id~= nil then
	            DestroyTable(t_id)
	end
end
event_table = {
	[QTABLE_LBUTTONDOWN] = "Нажали левую кнопку мыши",
	[QTABLE_RBUTTONDOWN] = "Нажали правую кнопку мыши",
	[QTABLE_LBUTTONDBLCLK] = "Левый даблклик",
	[QTABLE_RBUTTONDBLCLK]  = "Правый даблклик",
	[QTABLE_SELCHANGED] ="Изменилась строка",
	[QTABLE_CHAR] = "Символьная клавиша",
	[QTABLE_VKEY] = "Еще какая-то клавиша",
	[QTABLE_CONTEXTMENU] = "Контекстное меню",
	[QTABLE_MBUTTONDOWN] = "Нажали на колесико мыши",
	[QTABLE_MBUTTONDBLCLK] = "Даблклик колесом",
	[QTABLE_LBUTTONUP] = "Отпустили левую кнопку мыши",
	[QTABLE_RBUTTONUP] = "Отпустили правую кнопку мыши",
	[QTABLE_CLOSE] = "Закрыли таблицу"
	}
function event_callback_str(t_id, msg, par1, par2)
	local str = fmt("%s, par1 = %d, par2 = %d", event_table[msg], par1, par2)
	SetWindowCaption(t_id, str)
	message(str)
end

local p_row = -1
local p_col = -1
function event_callback_color(t_id, msg, par1, par2)
	if par1==3 and par2 == 1 then
	           os.exit()
	end
	if msg == QTABLE_LBUTTONDOWN then
	            if p_col ~= -1 and p_col ~= -1 then
                                               SetColor(t_id, p_row, p_col, QTABLE_DEFAULT_COLOR, QTABLE_DEFAULT_COLOR, QTABLE_DEFAULT_COLOR, QTABLE_DEFAULT_COLOR)
	            end
	            SetColor(t_id, par1, par2, RGB(240, 128, 128), QTABLE_DEFAULT_COLOR, QTABLE_DEFAULT_COLOR, QTABLE_DEFAULT_COLOR)
	             p_row = par1
	             p_col = par2
	end
end

function main()
	
	data = {
                                      {"1", 2, 20130530},
	                 {"4", 5, 20130529},
	                  {"7", 8, 20130528}
	               }
	
         t_id = AllocTable()
         message (t_id)
         AddColumn(t_id, 1, "строка", true, QTABLE_CACHED_STRING_TYPE, 10)
         AddColumn(t_id, 2, "число", true, QTABLE_INT_TYPE, 10)
         AddColumn(t_id, 3, "дата", true, QTABLE_DATE_TYPE, 10)
        CreateWindow(t_id)
	
         for _, v in pairs(data) do
	  row = InsertRow(t_id, -1)
	  SetCell(t_id, row, 1, v[1])
	  SetCell(t_id, row, 2, string.format("value = %d",v[2]), v[2])
	  SetCell(t_id, row, 3, string.format("%04d - %02d - %02d",v[3]/10000, (v[3]%10000)/100, v[3]%100), v[3])
         end
	SetWindowCaption(t_id, "EXAMPLE")
	SetTableNotificationCallback(t_id, event_callback_str)
	sleep(5000)
	SetTableNotificationCallback(t_id, event_callback_color)
	SetCell(t_id, 3, 1, "DO NOT CLICK ME")
	SetTableNotificationCallback(t_id, dummy)
	
	while not stopped do
	       sleep(100)
	end
end

Пример реализации игры «Крестики-нолики»

	
--[[ TIC-TAC-TOE
by Evan Hahn (http://evanhahn.com/how-to-code-tic-tac-toe-and-a-lua-implementation/
--]]
 
----------------------------------------------
-- Configuration (change this if you wish!) --
----------------------------------------------
t_id=nil --grid  
-- Are they playable by human or computer-controlled?
PLAYER_1_HUMAN = true
PLAYER_2_HUMAN = false
 
-- Board size
BOARD_RANK = 3	-- The board will be this in both dimensions.
 
-- Display stuff
PLAYER_1 = "[x]"	-- Player 1 is represented by this. Player 1 goes first.
PLAYER_2 = "[o]"	-- Player 2 is represented by this.
EMPTY_SPACE = "[ ]"	-- An empty space is displayed like this.
DISPLAY_HORIZONTAL_SEPARATOR = "-"	-- Horizontal lines look like this.
DISPLAY_VERTICAL_SEPARATOR = " | "	-- Vertical lines look like this
 
 
--[[ ###################################################################
     ####   Don't mess with things below here unless you are brave  ####
     ################################################################### --]]
 
------------------------
-- More configuration --
------------------------
 
MAX_BOARD_RANK = 100	-- Won't run above this number. Prevents crashes.
 
-------------------------------------------------------
-- Don't run if the board is larger than the maximum --
-------------------------------------------------------
 
if BOARD_RANK > MAX_BOARD_RANK then os.exit(0) end
 
-----------------------------
-- Create board (2D table) --
-----------------------------
 
space = {}
for i = 0, (BOARD_RANK - 1) do
	space[i] = {}
	for j = 0, (BOARD_RANK - 1) do
		space[i][j] = nil	-- start each space with nil
	end
end
 
---------------------
-- Board functions --
---------------------
 
-- get the piece at a given spot
function getPiece(x, y)
	return space[x][y]
end
 
-- get the piece at a given spot; if nil, return " "
-- this is useful for output.
function getPieceNoNil(x, y)
	if getPiece(x, y) ~= nil then
		return getPiece(x, y)
	else
		return EMPTY_SPACE
	end	
end
 
-- is that space empty?
function isEmpty(x, y)
	if getPiece(x, y) == nil then
		return true
	else
		return false
	end
end
 
-- place a piece there, but make sure nothing is there already.
-- if you can't play there, return false.
function placePiece(x, y, piece)
	if isEmpty(x, y) == true then
		space[x][y] = piece
		return true
	else
		return false
	end
end
 
-- is the game over?
function isGameOver()
	if checkWin() == false then	-- if there is no win...
		for i = 0, (BOARD_RANK - 1) do	-- is the board empty?
			for j = 0, (BOARD_RANK - 1) do
				if isEmpty(i, j) == true then return false end
			end
		end
		return true
	else	-- there is a win; the game is over
		return true
	end
end
 
-- create a string made up of a certain number of smaller strings
-- this is useful for the display.
function repeatString(to_repeat, amount)
	if amount <= 0 then return "" end
	local to_return = ""
	for i = 1, amount do
		to_return = to_return .. to_repeat
	end
	return to_return
end
 
-- display the board.
-- this uses the configuration file pretty much entirely.
function displayBoard()
	for i = (BOARD_RANK - 1), 0, -1 do
		for j = 0, (BOARD_RANK - 1) do	-- generate that row
			local piece = getPieceNoNil(j, i)
			SetCell(t_id, i+1, j+1, piece)
		end
	end
end
 
-------------------------------------------------
-- Create regions (I admit this is a bit ugly) --
-------------------------------------------------
 
-- declare region and a number to increment
region = {}
region_number = 0
 
-- vertical
for i = 0, (BOARD_RANK - 1) do
	region[region_number] = {}
	for j = 0, (BOARD_RANK - 1) do
		region[region_number][j] = {}
		region[region_number][j]["x"] = i
		region[region_number][j]["y"] = j
	end
	region_number = region_number + 1
end
 
-- horizontal
for i = 0, (BOARD_RANK - 1) do
	region[region_number] = {}
	for j = 0, (BOARD_RANK - 1) do
		region[region_number][j] = {}
		region[region_number][j]["x"] = j
		region[region_number][j]["y"] = i
	end
	region_number = region_number + 1
end
 
-- diagonal, bottom-left to top-right
region[region_number] = {}
for i = 0, (BOARD_RANK - 1) do
	region[region_number][i] = {}
	region[region_number][i]["x"] = i
	region[region_number][i]["y"] = i
end
region_number = region_number + 1
 
-- diagonal, top-left to bottom-right
region[region_number] = {}
for i = (BOARD_RANK - 1), 0, -1 do
	region[region_number][i] = {}
	region[region_number][i]["x"] = BOARD_RANK - i - 1
	region[region_number][i]["y"] = i
end
region_number = region_number + 1
 
----------------------
-- Region functions --
----------------------
 
-- get a region
function getRegion(number)
	return region[number]
end
 
-- check for a win in a particular region.
-- returns a number representation of the region. occurrences of player 1
-- add 1, occurrences of player 2 subtract 1. so if there are two X pieces,
-- it will return 2. one O will return -1.
function checkWinInRegion(number)
	local to_return = 0
	for i, v in pairs(getRegion(number)) do
		local piece = getPiece(v["x"], v["y"])
		if piece == PLAYER_1 then to_return = to_return + 1 end
		if piece == PLAYER_2 then to_return = to_return - 1 end
	end
	return to_return
end
 
-- check for a win in every region.
-- returns false if no winner.
-- returns the winner if there is one.
function checkWin()
	for i in pairs(region) do
		local win = checkWinInRegion(i)
		if math.abs(win) == BOARD_RANK then
			if win == math.abs(win) then
				return PLAYER_1
			else
				return PLAYER_2
			end
		end
	end
	return false
end
 
------------------
-- UI Functions --
------------------
 
-- human play
function humanPlay(piece)
	message("Human turn")
	displayBoard()
	local placed = false
	while placed == false do	-- loop until they play correctly
		sleep(100)
		if g_X ~= -1 and g_Y ~=-1 then
			local x = tonumber(g_Y)-1
			local y = tonumber(g_X)-1
			g_X = -1
			g_Y= -1
			message("clicked in " .. x .. " and " .. y)
			placed = placePiece(x, y, piece)
			if placed == false then
				message("I'm afraid you can't play there!")
			end
		end
	end
	displayBoard()
	
end
 
-- AI play
function AIPlay(piece)
	
	-- am I negative or positive?
	local me = 0
	if piece == PLAYER_1 then me = 1 end
	if piece == PLAYER_2 then me = -1 end
	
	-- look for a region in which I can win
	for i in pairs(region) do
		local win = checkWinInRegion(i)
		if win == ((BOARD_RANK - 1) * me) then
			for j, v in pairs(getRegion(i)) do
				if isEmpty(v["x"], v["y"]) == true then
					placePiece(v["x"], v["y"], piece)
					return
				end
			end
		end
	end
	
	-- look for a region in which I can block
	for i in pairs(region) do
		local win = checkWinInRegion(i)
		if win == ((BOARD_RANK - 1) * (me * -1)) then
			for j, v in pairs(getRegion(i)) do
				if isEmpty(v["x"], v["y"]) == true then
					placePiece(v["x"], v["y"], piece)
					return
				end
			end
		end
	end
	
	-- play first empty space, if no better option
	for i = 0, (BOARD_RANK - 1) do
		for j = 0, (BOARD_RANK - 1) do
			if placePiece(i, j, piece) ~= false then return end
		end
	end
	
end
g_X=-1
g_Y=-1
function event_callback(t_id, msg, par1, par2)
	if msg == QTABLE_LBUTTONDOWN then
		g_X = par1
		g_Y = par2
	end
end

old_message = message
local fmt = string.format
function message(v, t)
	t= t or 1
	old_message(tostring(v), t)
end

function main()
	t_id = AllocTable()
	AddColumn(t_id, 1, "", true, QTABLE_CACHED_STRING_TYPE, 5)
	AddColumn(t_id, 2, "", true, QTABLE_CACHED_STRING_TYPE, 5)
	AddColumn(t_id, 3, "", true, QTABLE_CACHED_STRING_TYPE, 5)
	CreateWindow(t_id)
	for i=1, 3 do
		row = InsertRow(t_id, -1)
		SetCell(t_id, row, 1, "[ ]")
		SetCell(t_id, row, 2, "[ ]")
		SetCell(t_id, row, 3, "[ ]")
	end
	SetTableNotificationCallback(t_id, event_callback)
	message("Welcome to Tic-Tac-Toe!")
 
-- play the game until someone wins
	while true do
		sleep(100)
	-- break if the game is won
		if isGameOver() == true then 
		 break 
		end
	-- player 1
		if PLAYER_1_HUMAN == true then 
			humanPlay(PLAYER_1)
		else 
			AIPlay(PLAYER_1) 
		end
	
		if isGameOver() == true then 
			break 
		end
	
		if PLAYER_2_HUMAN == true then 
			humanPlay(PLAYER_2)
		else 
			AIPlay(PLAYER_2) 
		end
	end
 
-- show the final board
	displayBoard()
 
-- write who won, or if there is a tie
	win = checkWin()
	if win == false then
		message("Tie game!\n")
	else
		message(win)
		message(" wins!\n")
	end
end