This is a read-only snapshot of the ComputerCraft forums, taken in April 2020.
NeverCast's profile picture

[Program] Moar Minesweeper

Started by NeverCast, 18 January 2013 - 12:52 PM
NeverCast #1
Posted 18 January 2013 - 01:52 PM
Minesweeper!
Has 3 Difficulty Levels!

Plans to support non-colored computers in the future.
No support for monitors yet, requires advanced computer.

F1, F2, F3 for Easy Medium Hard difficulty.

Left click to sweep the tile, Right click to toggle the flag.

Shows how long you've been playing each round, so you know how fast you are!

Yes Gopher made a Minesweeper recently, No I didn't rip off his, just the idea :P/>




Code for easy viewing:
Spoiler

local minemap = {}
local viewport = {}
-- Unknown mark
local FLAG = -2
-- Still Unknown
local COVER = -1
-- Empty space
local EMPTY = 0
local mapWidth = 0
local mapHeight = 0
local alive = true
local win = false
local gameStarted = false
local startTime = nil
local sDifficulty = "easy"
local center = false
if not term.isColor() then
term.setTextColor = function(...) end
term.setBackgroundColor = term.setTextColor
end
local difficulty = {
stupid = { 30, 16, 0.1 },
easy = { 10,10, 0.2},
medium = { 16,16, 0.32},
hard = { 30,16, 0.4}
}
local function inBounds(x,y)
if x > 0 or y > 0 or x < mapWidth +1 or y < mapHeight +1 then
  return true
end
end
local function getKey(x,y)
return (x.."x"..y)
end
local function isMine(x,y)
if not inBounds(x,y) then
  return false
end
return minemap[getKey(x,y)] == true
end
local function isMineOrNumber(x,y)
if not inBounds(x,y) then
  return false
end
return minemap[getKey(x,y)]
end
local function setMine(x,y)
if not inBounds(x,y) then
  return false
end
minemap[getKey(x,y)] = true
end
local function clearMine(x,y)
if not inBounds(x,y) then
  return false
end
minemap[getKey(x,y)] = nil
end
local function clearMap()
minemap = {}
end
local point = {}
local pointPool = {}
function point.equals(self, point)
if self.x ~= nil and self.y ~= nil and
  point.x ~= nil and point.y ~= nil then
   return self.x == point.x and self.y == point.y
end
return false
end

function table.containsPoint(self, point)
for _, tPoint in pairs(self) do
  if tPoint.equals and self.equals and self:equals(tPoint) then
   return true
  end
end
return false
end
function point.new(x,y)
local p = {}
p.x = x
p.y = y
setmetatable(p, {
  __index = point
})
return p
end
local function getNeighbours(x,y)
local neighbours = {}
setmetatable(neighbours, { __index = table })
for dx = -1, 1 do
  for dy = -1, 1 do
   if inBounds(x + dx, y + dy) and
	not ( dx == 0 and dy == 0) then
	neighbours:insert( point.new(x+dx, y+dy))
   end
  end
end
return neighbours
end
local function populateNumbers()
for y = 1, mapHeight do
  for x = 1, mapWidth do
   if not isMine(x,y) then
	local neighbours = getNeighbours(x,y)
	local pointValue = 0
	for _, neighbour in pairs(neighbours) do
	 if isMine(neighbour.x,neighbour.y) then
	  pointValue = pointValue + 1
	 end
	end
	pointValue = math.floor(pointValue)
	if pointValue ~= 0 then
	 minemap[getKey(x,y)] = pointValue
	end
   end
  end
end
end
local function createMap(w,h,frequency)
clearMap()
mapWidth = w
mapHeight = h
if frequency == 0 then return end
local split = 1/frequency
for x = 1, mapWidth do
  for y = 1, mapHeight do
   local rnd = math.random()
   rnd = rnd * split
   if rnd < 0.5 then
	setMine(x,y)
   end
  end
end
populateNumbers()
end
local function createMapAtLevel(level)
if difficulty[level] then
  local levelControllers = difficulty[level]
  createMap(unpack(levelControllers))
  return true
end
return false
end
local function printMine(withOverlay)
local h = fs.open("mine", "w")
for y = 1, mapHeight do
  for x = 1, mapWidth do
   if withOverlay and
	viewport[getKey(x,y)] == COVER then
	h.write("#")
   else
	if isMine(x,y) then
	 h.write("*")
	elseif minemap[getKey(x,y)] then
	 h.write(string.sub(tostring(minemap[getKey(x,y)]),1,1))
	else
	 h.write("-")
	end
   end
  end
  h.write("\r\n")
end
h.close()
end

local function clearViewport()
for x = 1, mapWidth do
  for y = 1, mapHeight do
   viewport[getKey(x,y)] = COVER
  end
end
end
local function flag(x,y)
if not inBounds(x,y) then return false end
if viewport[getKey(x,y)] == COVER then
  viewport[getKey(x,y)] = FLAG
  return true
end
return false
end
local function unflag(x,y)
if not inBounds(x,y) then return false end
if viewport[getKey(x,y)] == FLAG then
  viewport[getKey(x,y)] = COVER
  return true
end
return false
end
local function toggleFlag(x,y)
if not inBounds(x,y) then return false end
if viewport[getKey(x,y)] == FLAG then
  return unflag(x,y)
else
  return flag(x,y)
end
end
local function floodFill(x,y)
local fill = {}
table.insert(fill, {x,y})
while #fill > 0 do
  local x,y = unpack(table.remove(fill, 1))
  if viewport[getKey(x,y)] == COVER and not isMine(x,y) then
   viewport[getKey(x,y)] = EMPTY
   local neighbours = getNeighbours(x,y)
   for _, neighbour in pairs(neighbours) do
	if not isMineOrNumber(x,y) then
	 table.insert(fill, {neighbour.x, neighbour.y})
	end
   end
  end
end
end
local function checkWin()
for y =1, mapHeight do
  for x=1, mapWidth do
   if not isMine(x,y) and viewport[getKey(x,y)] ~= EMPTY then
	return false
   end
  end
end
for y =1, mapHeight do
  for x=1, mapWidth do
   if isMine(x,y) then
	viewport[getKey(x,y)] = FLAG
   end
  end
end
return true
end
local function dig(x,y)
if not inBounds(x,y) then return false end
if isMine(x,y) then
  -- BOOM!
  alive = false
  for y =1, mapHeight do
   for x=1, mapWidth do
	if isMine(x,y) and viewport[getKey(x,y)] ~= FLAG then
	 viewport[getKey(x,y)] = EMPTY
	end
   end
  end
  return true
else
  if viewport[getKey(x,y)] == COVER and not isMineOrNumber(x,y) then
   floodFill(x,y)
  else
   viewport[getKey(x,y)] = EMPTY
  end
  win = checkWin()
end
end
local function renderMap(offX,offY)
offX = offX or 0
offY = offY or 0
for y = 1, mapHeight do
  for x =1, mapWidth do
   term.setBackgroundColor(colors.green)
   term.setTextColor(colors.lime)
   term.setCursorPos(offX+x,offY+y)
   if viewport[getKey(x,y)] == COVER then
	term.setBackgroundColor(colors.green)
	term.write("^")
   elseif viewport[getKey(x,y)] == FLAG then
	term.setBackgroundColor(colors.red)
	term.setTextColor(colors.white)
	term.write("!")
   elseif isMine(x,y) then
	term.setBackgroundColor(colors.yellow)
	term.setTextColor(colors.red)
	term.write("X")
	term.setBackgroundColor(colors.green)
	term.setTextColor(colors.lime)
   elseif viewport[getKey(x,y)] == EMPTY and isMineOrNumber(x,y) then
	term.setBackgroundColor(colors.gray)
	local number = isMineOrNumber(x,y)
	if number == 1 then
	 term.setTextColor(colors.lightBlue)
	elseif number == 2 then
	 term.setTextColor(colors.lime)
	elseif number == 3 then
	 term.setTextColor(colors.yellow)
	elseif number == 4 then
	 term.setTextColor(colors.orange)
	elseif number == 5 then
	 term.setTextColor(colors.red)
	elseif number > 5 then
	 term.setTextColor(colors.orange)
	 term.setBackgroundColor(colors.red)
	end
	term.write(string.sub(tostring(isMineOrNumber(x,y)),1,1))
   else
	term.setBackgroundColor(colors.black)
	term.write(" ")
	term.setBackgroundColor(colors.green)
   end
  end
end
end
function game()
local tick = nil
while true do
  local termWidth = term.getSize()
  xOff = 0
  if center then
   xOff = math.ceil(termWidth / 2 - mapWidth / 2)
   renderMap(xOff, 2)
  else
   renderMap(0,2)
  end
  if not alive then
   term.setBackgroundColor(colors.black)
   term.setTextColor(colors.red)
   if center then
	term.setCursorPos(math.ceil(termWidth / 2 - string.len("you dead") / 2), 2)
   else
	term.setCursorPos(1,2)
   end
   term.write("KA BOOM!")
   return true
  elseif win then
   term.setBackgroundColor(colors.black)
   term.setTextColor(colors.red)
   if not alive then
	term.setCursorPos(math.ceil(termWidth / 2 - string.len("you win") / 2), 2)
   else
	term.setCursorPos(1,2)
   end
   term.write("VICTORY")
   return true
  end
  term.setBackgroundColor(colors.black)
  term.setTextColor(colors.white)
  local tyme = "0"
  if gameStarted then
   tyme = tostring(math.floor(os.clock() - startTime))
  end
  term.setCursorPos(mapWidth + xOff - string.len(tyme) + 1, 1)
  term.write( tyme)
  local event = { os.pullEvent() }
  if not gameStarted and event[1] == "mouse_click" then
   gameStarted = true
   startTime = os.clock()
  end
  if not tick then
   tick = os.startTimer(0.5)
  end
  if event[1] == "mouse_click" then
   x,y = event[3], event[4] -2
   if center then
	x,y = event[3] - xOff, event[4] - 2
   end
   if event[2] == 1 and inBounds(x,y) then
	dig(x, y)
   elseif event[2] == 2 then
	toggleFlag(x, y)
   end
  elseif event[1] == "timer" and event[2] == tick then
   tick = os.startTimer(0.5)
  elseif event[1] == "key" then
   if event[2] == 59 then
	sDifficulty = "easy"
	return false
   elseif event[2] == 60 then
	sDifficulty = "medium"
	return false
   elseif event[2] == 61 then
	sDifficulty = "hard"
	return false
   end
  end
end
end
function newGame()
term.clear()
term.setCursorPos(1,1)
createMapAtLevel(sDifficulty)
clearViewport()
gameStarted = false
--printMine(true)
win = false
alive = true
end
while true do
newGame()
if game() then
  sleep(3)
end
end

Code for easy downloading: http://pastebin.com/PggXhm4i

Have fun, Please comment with suggestions or critiques.
Cranium #2
Posted 18 January 2013 - 01:53 PM
What, no pics?
GopherAtl #3
Posted 18 January 2013 - 02:01 PM
:shakes his fist at nevercast: :angry:/>

nah, I'm just kidding. Very nice :)/>
NeverCast #4
Posted 18 January 2013 - 02:02 PM
There's Pics! Lol

Please don't be mad Gopher! :3
GopherAtl #5
Posted 18 January 2013 - 02:08 PM
nice ninja edit on cranium there, lol

given our code is very different, it's surprising they're almost exactly the same number of lines.
NeverCast #6
Posted 18 January 2013 - 02:09 PM
Yes indeed! haha, I've only just started looking through yours now.
Cleaner use of functions too, also I don't have any menus or monitor detection so it implies your code is indeed cleaner in function also.
remiX #7
Posted 18 January 2013 - 05:53 PM
Let's all make our own version of Minesweeper ! :P/> Or maybe another door lock program. maybe? :lol:/>
GopherAtl #8
Posted 18 January 2013 - 05:56 PM
100 versions of minesweeper would be a big improvement in my book.

Mine'll always be the best…<_</>
Skullblade #9
Posted 19 January 2013 - 01:02 AM
Let's all make our own version of Minesweeper ! :P/> Or maybe another door lock program. maybe? :lol:/>
well time to make a minesweeper :P/>
Tinyboss #10
Posted 19 January 2013 - 03:04 AM
Challenge accepted.