ComputerCraft Archive

connect4

computer utility LDDestroier github

Description

A collection of all my ComputerCraft programs and the APIs they use. This is mostly just to get them the fuck off of pastebin, and also to ensure that API owners don't change things to break my precious programs...!

Installation

Copy one of these commands into your ComputerCraft terminal:

wget:wget https://raw.githubusercontent.com/LDDestroier/CC/master/connect4.lua connect4
Archive:wget https://cc.shobie.xyz/cc/get/gh-LDDestroier-CC-connect4 connect4
Quick Install: wget https://cc.shobie.xyz/cc/get/gh-LDDestroier-CC-connect4 connect4

Usage

Run: connect4

Tags

none

Source

View Original Source

Code Preview

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()