Spoiler
--[[
Vote v1.0 by Espen
An example code for a voting system, inspired by ewart4fun
Thread: http://www.computercraft.info/forums2/index.php?/topic/5807-lua-voting-program/
--]]
-- [[ BEGINNING of FUNCTION DEFINITIONS ]] ---------------------------
-- Writes text centered, x- and y-axis position can be offset, and the line to be written can be cleared beforehand.
local function writeCentered( sText, nOffsetX, nOffsetY, color, bClearLine )
local maxX, maxY = term.getSize()
if nOffsetX == null then nOffsetX = 0 end
local centeredX = (maxX / 2) - (string.len( sText ) / 2) + nOffsetX
if nOffsetY == null then nOffsetY = 0 end
local centeredY = (maxY / 2) + nOffsetY
term.setCursorPos( centeredX, centeredY )
if bClearLine then term.clearLine() end
if color and term.isColor() then term.setTextColor( color ) end
write( sText )
if color and term.isColor() then term.setTextColor( colors.white ) end
end
--[[
Returns:
0 - If login is ok.
1 - If login failed.
2 - If user already voted.
]]
local function checkLogin( sUser, sPwd )
--[[
^%a = first char has to be a letter
%w* = then there can follow an arbitrary number of alphanumeric characters
: = then a colon follows (which is used in the passwd file to delimit the usernames from their passwords)
[01] = then either a 0 or a 1 follows
: = then another colon follows (which is used in the passwd file to delimit the usernames from their passwords)
.*$ = finally the pattern ends with an arbitrary number of any characters, i.e. empty passwords are allowed as well (use .+$ to ignore users with empty passwords).
( ) = Everything we encase in brackets will be extracted and returned as a separate value.
]]
local credentials_pattern = "^(%a%w*):(/>/>[01]):(/>/>.*)$" -- Example: "user123:0:secret"
local bLoginOk = -1
local hFile = fs.open( ".passwd", "r" )
local line = hFile.readLine()
while line do
local user, hasVoted, pwd = string.match( line, credentials_pattern )
if ( user and pwd ) and ( sUser == user ) then
if ( sPwd == pwd ) then
if ( hasVoted == "0" ) then
-- User found and password correct. Has not yet voted.
bLoginOk = 0
break
else
-- User found and password correct. Has already voted.
bLoginOk = 1
break
end
else
-- Password incorrect.
--bLoginOk = -1 -- Unnecessary in the current code, as bLoginOk will not have changed from -1 when the program reaches this point.
break
end
end
-- Read the next line.
line = hFile.readLine()
end
hFile:close()
return bLoginOk
end
local function getLogin()
local usrCurPosX, usrCurPosY, pwdCurPosX, pwdCurPosY
local username = "", password
while username == "" do
writeCentered( "Username: ", -5, 0, nil, true )
usrCurPosX, usrCurPosY = term.getCursorPos()
writeCentered( "Password: ", -5, 1, nil, true )
pwdCurPosX, pwdCurPosY = term.getCursorPos()
term.setCursorPos( usrCurPosX, usrCurPosY )
username = read()
if username == "" then term.clear() end
end
term.setCursorPos( pwdCurPosX, pwdCurPosY )
password = read("*")
return username, password
end
-- Returns a table with candidates.
local function getCandidates()
--[[
[^:]+ = Each candidate name has to be comprised of at least one character, except colon.
]]
local candidate_pattern = "[^:]+"
local tCandidates = {}
local candidate = nil
local hFile = fs.open( ".candidates", "r" )
local line = hFile.readLine()
while line do
candidate = string.match( line, candidate_pattern )
if candidate then
table.insert( tCandidates, candidate )
end
-- Read the next line.
line = hFile.readLine()
end
hFile:close()
return tCandidates
end
local function printVotingScreen( candidateList )
term.clear()
term.setCursorPos(1, 2)
print( "Place your vote (type the corresponding numbers):n" )
for index, candidate in ipairs( candidateList ) do
print( "t("..index..") - "..candidate )
end
print( "nt(X) - Logout (vote later)." )
end
local function getVote( candidateList )
while true do
printVotingScreen( candidateList )
local _, sChoice = os.pullEvent( "char" )
local nChoice = tonumber( sChoice )
-- Ask for confirmation of choice.
if candidateList[ nChoice ] then
write( "nYou chose: ")
if term.isColor() then
term.setTextColor( colors.yellow )
print (candidateList[ nChoice ] )
term.setTextColor( colors.white )
else
print (candidateList[ nChoice ] )
end
print( "(A)ccept or (C)ancel this choice." )
while true do
local _, sCommit = os.pullEvent( "char" )
if sCommit and string.lower(sCommit) == "a" then return nChoice end
if sCommit and string.lower(sCommit) == "c" then break end
end
end
-- Logout
if sChoice and string.lower(sChoice) == "x" then break end
end
end
local function setHasVoted( voter )
local users = {}
local username, hasVoted, password
local credentials_pattern = "^(%a%w*):(/>/>[01]):(/>/>.*)$"
-- Read old values into table 'users'.
local hFile = fs.open( ".passwd", "r" )
local line = hFile.readLine()
while line do
username, hasVoted, password = string.match( line, credentials_pattern )
if username and hasVoted and password then
users[username] = { hasVoted, password }
end
line = hFile.readLine()
end
hFile.close()
-- Write changes to file.
local hFile = fs.open( ".passwd", "w" )
for user, _ in pairs(users) do
if user == voter then
hFile.writeLine(user..":1:"..users[user][2])
else
hFile.writeLine(user..":"..users[user][1]..":"..users[user][2]) -- users[user][1] is hasVoted and users[user][2] is password.
end
end
hFile.close()
end
local function applyVote( tCandidateList, sVoter, nCandidateNum )
local vote = nil
local votes = {}
local votedCandidate
local votes_pattern = "^([^:]+):(/>/>%d+)$" -- Example: candidate3:27
local hFile = fs.open( ".votes", "r" )
local line = hFile.readLine()
while line do
candidate, vote = string.match( line, votes_pattern )
if candidate and vote then votes[candidate] = vote end
line = hFile.readLine()
end
votedCandidate = tCandidateList[ nCandidateNum ]
if not votedCandidate then print(tCandidateList[ 1 ]) error( "The voted candidate isn't in the list of eligible candidates. This shouldn't happen, please fix me." ) end
-- Initialize votes for candidate in case there are not votes for him yet.
if not votes[votedCandidate] then
votes[votedCandidate] = 0
end
-- Now finally increment his votes by 1.
votes[votedCandidate] = votes[votedCandidate] + 1
-- Write the changes back to file.
local hFile = fs.open( ".votes", "w" )
for candidate, vote in pairs(votes) do
hFile.writeLine( candidate..":"..vote )
end
hFile.close()
-- Set the hasVoted flag for the voter.
setHasVoted( sVoter )
end
local function main()
term.clear()
local sUser, sPassword
local candidateList
local vote
local loginState
local validLogin = false
-- Check for valid user.
while not validLogin do
sUser, sPassword = getLogin()
-- Special login which quits the program to console. Remove if unwanted.
if sUser == "exit to" and sPassword == "console" then return false end
loginState = checkLogin( sUser, sPassword )
if loginState == 0 then
writeCentered( "[ Access Granted ]n", 0, 3, colors.lime, true )
validLogin = true
elseif loginState == -1 then
writeCentered( "[ Access Denied ]n", 0, 3, colors.red, true )
elseif loginState == 1 then
writeCentered( "[ Already Voted ]n", 0, 3, colors.purple, true )
end
sleep(1) -- artificial cooldown before next try
end
-- Get the list of candidates and let the current user make a vote.
candidateList = getCandidates()
if #candidateList < 1 then
term.clear()
writeCentered( "No candidates to vote for." )
writeCentered( "Please try again later.", 0, 1 )
writeCentered( "Press ENTER to dismiss...", 0, 4 )
local _, enter
while enter ~= 28 do
_, enter = os.pullEvent( "key" )
end
return true
end
-- Get a vote and apply it.
vote = getVote( candidateList )
if type(vote) == "number" then
applyVote( candidateList, sUser, vote )
term.clear()
writeCentered( "Thank you for voting.", 0, 0, colors.yellow )
writeCentered( "Have a nice day!", 0, 1, colors.yellow )
sleep(3)
end
return true
end
-- [[ END of FUNCTION DEFINITIONS ]] ---------------------------
-- Prevent termination key-combo
nativePullEvent = os.pullEvent
os.pullEvent = os.pullEventRaw
-- Program loop
local running = true
while running do
running = main()
end
-- Restore native os.pullEvent
os.pullEvent = nativePullEvent
Alternative Location: