ldd-CC/connect4.lua

333 lines
7.2 KiB
Lua

local scr_x, scr_y = term.getSize()
local midX, midY = .5 * scr_x, .5 * scr_y
local origTX, origBG = term.getTextColor(), term.getBackgroundColor()
local winLength = 4
local sleepDelay = 0.05
local moveCount = 0
local board = {} -- connect 4 board; formatted like board[y][x]
local block = {} -- bottom blockage; formatted like block[x]
local waiting = false
local boardX, boardY = 7, 6 -- size of board
local palette = {
bg = colors.black, -- color of backdrop
board = colors.white, -- color of board
txt = colors.white -- color of text
}
local tiles = {
["bl"] = palette.bg, -- blank space
["P1"] = colors.red, -- player 1
["P2"] = colors.blue -- player 2
}
local you = "P1"
local nou = "P2"
local cwrite = function(text, y, doClear)
local cx, cy = term.getCursorPos()
term.setCursorPos(midX - math.floor(#text / 2), y or cy)
if doClear then term.clearLine() end
term.write(text)
end
local cblit = function(char, text, back, y, doClear)
local cx, cy = term.getCursorPos()
term.setCursorPos(midX - math.floor(#text / 2), y or cy)
if doClear then term.clearLine() end
term.blit(char, text, back)
end
local resetBoard = function()
board = {}
for y = 1, boardY do
board[y] = {}
for x = 1, boardX do
board[y][x] = {"bl", 0} -- owner, half-in mod
end
end
for x = 1, boardX do
block[x] = true
end
end
local addPiece = function(owner, x)
if board[1][x][1] == "bl" then
board[1][x] = {owner, 0, x, 1}
return true
else
return false
end
end
local moveTilesDown = function()
local settled = true -- allows for animated falling tiles
for y = boardY, 1, -1 do
for x = 1, boardX do
if board[y][x][1] ~= "bl" then
if board[y][x][2] == -1 then
board[y][x][2] = 0
settled = false
elseif (y + 1 <= boardY) then
if board[y + 1][x][1] == "bl" then
if board[y][x][2] == 0 then
board[y][x][2] = 1
settled = false
elseif board[y][x][2] == 1 then
board[y + 1][x] = {board[y][x][1], -1, x, y + 1}
board[y][x] = {"bl", 0, x, y}
settled = false
end
end
elseif not block[x] then
if board[y][x][2] == 0 then
board[y][x][2] = 1
settled = false
else
board[y][x] = {"bl", 0, x, y}
end
end
end
end
end
return settled
end
resetBoard()
local tileChar = {
{
"\131\148",
"\143\133",
},
{
"10",
"00",
},
{
"01",
"11",
}
}
local to_blit = {
[0] = " ",
[colors.white] = "0",
[colors.orange] = "1",
[colors.magenta] = "2",
[colors.lightBlue] = "3",
[colors.yellow] = "4",
[colors.lime] = "5",
[colors.pink] = "6",
[colors.gray] = "7",
[colors.lightGray] = "8",
[colors.cyan] = "9",
[colors.purple] = "a",
[colors.blue] = "b",
[colors.brown] = "c",
[colors.green] = "d",
[colors.red] = "e",
[colors.black] = "f",
}
term.setBackgroundColor(palette.bg)
term.clear()
local checkIfWinner = function()
local conditions = {}
-- check horizontal
for y = 1, boardY do
for x = 1, boardX - winLength + 1 do
conditions[#conditions+1] = {}
for w = 0, winLength - 1 do
conditions[#conditions][w+1] = board[y][x+w]
end
end
end
-- check vertical
for y = boardY - winLength + 1, 1, -1 do
for x = 1, boardX do
conditions[#conditions+1] = {}
for w = 0, winLength - 1 do
conditions[#conditions][w+1] = board[y+w][x]
end
end
end
-- check diagonals
for y = 1, boardY - winLength + 1 do
for x = 1, boardX - winLength + 1 do
conditions[#conditions+1] = {}
conditions[#conditions+1] = {}
for w = 0, winLength - 1 do
conditions[#conditions-1][w+1] = board[y+(winLength-w-1)][x+w]
conditions[#conditions][w+1] = board[y+w][x+w]
end
end
end
local winner, check
for set = 1, #conditions do
winner = true
check = conditions[set][1][1]
for piece = 2, #conditions[set] do
if conditions[set][piece][1] == "bl" or conditions[set][piece][1] ~= check then
winner = false
break
end
end
if winner then
return conditions[set][1][1], conditions[set]
end
end
return false
end
local renderBoard = function()
local tileColRep = {
["1"] = to_blit[palette.board]
}
local cx, cy
for y = 1, boardY + 1 do
if y == boardY + 1 then
term.setTextColor(palette.txt)
for x = 1, boardX do
term.setCursorPos(midX - (boardX) + (x - 1) * #tileChar[1][1], 4)
term.write(x)
end
cwrite("SPACE to clear", scr_y, false)
else
for ymod = 1, #tileChar[1] do
for x = 0, boardX do
cx = x * #tileChar[1][1] + (midX - boardX) - 2
cy = y * #tileChar[1] + ymod + (midY - boardY) - 1
if x == 0 then
term.setCursorPos(cx + 1, cy)
term.blit("\149", to_blit[palette.bg], to_blit[palette.board])
else
term.setCursorPos(cx, cy)
if (board[y][x][2] == 0) or (board[y][x][2] == -1 and ymod == 1) or (board[y][x][2] == 1 and ymod == 2) then
tileColRep["0"] = to_blit[tiles[ board[y][x][1] ]]
elseif board[y][x][2] == 2 then
tileColRep["0"] = to_blit[palette.board]
else
tileColRep["0"] = to_blit[tiles["bl"]]
end
term.blit(
tileChar[1][ymod],
tileChar[2][ymod]:gsub(".", tileColRep),
tileChar[3][ymod]:gsub(".", tileColRep)
)
end
end
end
end
end
end
local getInput = function()
local evt
while true do
evt = {os.pullEvent()}
if evt[1] == "char" then
if tonumber(evt[2]) then
if tonumber(evt[2]) >= 1 and tonumber(evt[2]) <= boardX then
if not waiting then
if board[1][tonumber(evt[2])][1] == "bl" then
addPiece(you, tonumber(evt[2]))
moveCount = moveCount + 1
you, nou = nou, you
waiting = true
end
end
end
end
if evt[2] == " " then
os.queueEvent("clear_board")
for y = 1, boardY do
for x = 1, boardX do
board[y][x][2] = 0
end
end
for x = 1, boardX do
block[x] = false
sleep(0.05)
end
moveCount = 0
you, nou = "P1", "P2"
sleep(1)
for x = 1, boardX do
block[x] = true
end
elseif evt[2] == "q" then
return "exit"
end
end
end
end
local main = function()
local winner, winPieces
while true do
renderBoard()
while not moveTilesDown() do
sleep(sleepDelay)
renderBoard()
end
winner, winPieces = checkIfWinner()
term.setTextColor(palette.txt)
if winner then
cblit(
"Winner: " .. winner,
to_blit[palette.txt]:rep(8) .. to_blit[tiles[winner]]:rep(#winner),
to_blit[palette.bg]:rep(8 + #winner),
1,
true
)
parallel.waitForAny(function()
while true do
for p = 1, #winPieces do
board[winPieces[p][4]][winPieces[p][3]][2] = 0
end
renderBoard()
sleep(0.3)
for p = 1, #winPieces do
board[winPieces[p][4]][winPieces[p][3]][2] = 2
end
renderBoard()
sleep(0.2)
end
end, function()
local evt
repeat
evt = {os.pullEvent()}
until evt[1] == "clear_board"
end)
elseif moveCount >= boardX * boardY then
cwrite("It's a tie.", 1, true)
waiting = true
else
waiting = false
cblit(
"It's " .. you .. "'s turn.",
to_blit[palette.txt]:rep(5) .. to_blit[tiles[you]]:rep(#you) .. to_blit[palette.txt]:rep(8),
to_blit[palette.bg]:rep(13 + #you),
1,
true
)
end
sleep(sleepDelay)
end
end
parallel.waitForAny(main, getInput)
cwrite("Thanks for playing!", 1, true)
term.setCursorPos(1, scr_y)
term.clearLine()