Guest User

NeverCast's MineSweeper

a guest
Jan 17th, 2013
82
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 9.05 KB | None | 0 0
  1. local minemap = {}
  2. local viewport = {}
  3.  
  4. -- Unknown mark
  5. local FLAG = -2
  6. -- Still Unknown
  7. local COVER = -1
  8. -- Empty space
  9. local EMPTY = 0
  10.  
  11. local mapWidth = 0
  12. local mapHeight = 0
  13.  
  14. local alive = true
  15. local win = false
  16. local gameStarted = false
  17. local startTime = nil
  18.  
  19. local sDifficulty = "easy"
  20.  
  21. local center = false
  22.  
  23. if not term.isColor() then
  24.     term.setTextColor = function(...) end
  25.     term.setBackgroundColor = term.setTextColor
  26. end
  27.  
  28. local difficulty = {
  29.     stupid = { 30, 16, 0.1 },
  30.     easy = { 10,10, 0.2},
  31.     medium = { 16,16, 0.32},
  32.     hard = { 30,16, 0.4}
  33. }
  34.  
  35. local function inBounds(x,y)
  36.     if x > 0 or y > 0 or x < mapWidth +1 or y < mapHeight +1 then
  37.         return true
  38.     end
  39. end
  40.  
  41. local function getKey(x,y)
  42.     return (x.."x"..y)
  43. end
  44.  
  45. local function isMine(x,y)
  46.     if not inBounds(x,y) then
  47.         return false
  48.     end
  49.     return minemap[getKey(x,y)] == true
  50. end
  51.  
  52. local function isMineOrNumber(x,y)
  53.     if not inBounds(x,y) then
  54.         return false
  55.     end
  56.     return minemap[getKey(x,y)]
  57. end
  58.  
  59. local function setMine(x,y)
  60.     if not inBounds(x,y) then
  61.         return false
  62.     end
  63.     minemap[getKey(x,y)] = true
  64. end
  65.  
  66. local function clearMine(x,y)
  67.     if not inBounds(x,y) then
  68.         return false
  69.     end
  70.     minemap[getKey(x,y)] = nil
  71. end
  72.  
  73. local function clearMap()
  74.     minemap = {}
  75. end
  76.  
  77. local point = {}
  78. local pointPool = {}
  79. function point.equals(self, point)
  80.     if self.x ~= nil and self.y ~= nil and
  81.         point.x ~= nil and point.y ~= nil then
  82.             return self.x == point.x and self.y == point.y
  83.     end
  84.     return false
  85. end
  86.  
  87.  
  88. function table.containsPoint(self, point)
  89.     for _, tPoint in pairs(self) do
  90.         if tPoint.equals and self.equals and self:equals(tPoint) then
  91.             return true
  92.         end
  93.     end
  94.     return false
  95. end
  96.  
  97. function point.new(x,y)
  98.     local p = {}
  99.     p.x = x
  100.     p.y = y
  101.     setmetatable(p, {
  102.         __index = point
  103.     })
  104.     return p
  105. end
  106.  
  107. local function getNeighbours(x,y)
  108.     local neighbours = {}
  109.     setmetatable(neighbours, { __index = table })
  110.     for dx = -1, 1 do
  111.         for dy = -1, 1 do
  112.             if inBounds(x + dx, y + dy) and
  113.                 not ( dx == 0 and dy == 0) then
  114.                 neighbours:insert( point.new(x+dx, y+dy))
  115.             end
  116.         end
  117.     end
  118.     return neighbours
  119. end
  120.  
  121. local function populateNumbers()
  122.     for y = 1, mapHeight do
  123.         for x = 1, mapWidth do
  124.             if not isMine(x,y) then
  125.                 local neighbours = getNeighbours(x,y)
  126.                 local pointValue = 0
  127.                 for _, neighbour in pairs(neighbours) do
  128.                     if isMine(neighbour.x,neighbour.y) then
  129.                         pointValue = pointValue + 1
  130.                     end
  131.                 end
  132.                 pointValue = math.floor(pointValue)
  133.                 if pointValue ~= 0 then
  134.                     minemap[getKey(x,y)] = pointValue
  135.                 end
  136.             end
  137.         end
  138.     end
  139. end
  140.  
  141. local function createMap(w,h,frequency)
  142.     clearMap()
  143.     mapWidth = w
  144.     mapHeight = h
  145.     if frequency == 0 then return end
  146.     local split = 1/frequency
  147.     for x = 1, mapWidth do
  148.         for y = 1, mapHeight do
  149.             local rnd = math.random()
  150.             rnd = rnd * split
  151.             if rnd < 0.5 then
  152.                 setMine(x,y)
  153.             end
  154.         end
  155.     end
  156.     populateNumbers()
  157. end
  158.  
  159. local function createMapAtLevel(level)
  160.     if difficulty[level] then
  161.         local levelControllers = difficulty[level]
  162.         createMap(unpack(levelControllers))
  163.         return true
  164.     end
  165.     return false
  166. end
  167.  
  168. local function printMine(withOverlay)
  169.     local h = fs.open("mine", "w")
  170.     for y = 1, mapHeight do
  171.         for x = 1, mapWidth do
  172.             if withOverlay and
  173.                 viewport[getKey(x,y)] == COVER then
  174.                 h.write("#")
  175.             else
  176.                 if isMine(x,y) then
  177.                     h.write("*")
  178.                 elseif minemap[getKey(x,y)] then
  179.                     h.write(string.sub(tostring(minemap[getKey(x,y)]),1,1))
  180.                 else
  181.                     h.write("-")
  182.                 end
  183.             end
  184.         end
  185.         h.write("\r\n")
  186.     end
  187.     h.close()
  188. end
  189.  
  190.  
  191. local function clearViewport()
  192.     for x = 1, mapWidth do
  193.         for y = 1, mapHeight do
  194.             viewport[getKey(x,y)] = COVER
  195.         end
  196.     end
  197. end
  198.  
  199. local function flag(x,y)
  200.     if not inBounds(x,y) then return false end
  201.     if viewport[getKey(x,y)] == COVER then
  202.         viewport[getKey(x,y)] = FLAG
  203.         return true
  204.     end
  205.     return false
  206. end
  207.  
  208. local function unflag(x,y)
  209.     if not inBounds(x,y) then return false end
  210.     if viewport[getKey(x,y)] == FLAG then
  211.         viewport[getKey(x,y)] = COVER
  212.         return true
  213.     end
  214.     return false
  215. end
  216.  
  217. local function toggleFlag(x,y)
  218.     if not inBounds(x,y) then return false end
  219.     if viewport[getKey(x,y)] == FLAG then
  220.         return unflag(x,y)
  221.     else
  222.         return flag(x,y)
  223.     end
  224. end
  225.  
  226. local function floodFill(x,y)
  227.     local fill = {}
  228.     table.insert(fill, {x,y})
  229.     while #fill > 0 do
  230.         local x,y = unpack(table.remove(fill, 1))
  231.         if viewport[getKey(x,y)] == COVER and not isMine(x,y) then
  232.             viewport[getKey(x,y)] = EMPTY
  233.             local neighbours = getNeighbours(x,y)
  234.             for _, neighbour in pairs(neighbours) do
  235.                 if not isMineOrNumber(x,y) then
  236.                     table.insert(fill, {neighbour.x, neighbour.y})
  237.                 end
  238.             end
  239.         end
  240.     end
  241. end
  242.  
  243. local function checkWin()
  244.     for y =1, mapHeight do
  245.         for x=1, mapWidth do
  246.             if not isMine(x,y) and viewport[getKey(x,y)] ~= EMPTY then
  247.                 return false
  248.             end
  249.         end
  250.     end
  251.     for y =1, mapHeight do
  252.         for x=1, mapWidth do
  253.             if isMine(x,y) then
  254.                 viewport[getKey(x,y)] = FLAG
  255.             end
  256.         end
  257.     end    
  258.     return true
  259. end
  260.  
  261. local function dig(x,y)
  262.     if not inBounds(x,y) then return false end
  263.     if isMine(x,y) then
  264.         -- BOOM!
  265.         alive = false
  266.         for y =1, mapHeight do
  267.             for x=1, mapWidth do
  268.                 if isMine(x,y) and viewport[getKey(x,y)] ~= FLAG then
  269.                     viewport[getKey(x,y)] = EMPTY
  270.                 end
  271.             end
  272.         end
  273.         return true
  274.     else
  275.         if viewport[getKey(x,y)] == COVER and not isMineOrNumber(x,y) then
  276.             floodFill(x,y)
  277.         else
  278.             viewport[getKey(x,y)] = EMPTY
  279.         end
  280.         win = checkWin()
  281.     end
  282. end
  283.  
  284. local function renderMap(offX,offY)
  285.     offX = offX or 0
  286.     offY = offY or 0
  287.     for y = 1, mapHeight do
  288.         for x =1, mapWidth do      
  289.             term.setBackgroundColor(colors.green)
  290.             term.setTextColor(colors.lime)
  291.             term.setCursorPos(offX+x,offY+y)
  292.             if viewport[getKey(x,y)] == COVER then
  293.                 term.setBackgroundColor(colors.green)
  294.                 term.write("^")
  295.             elseif viewport[getKey(x,y)] == FLAG then
  296.                 term.setBackgroundColor(colors.red)
  297.                 term.setTextColor(colors.white)
  298.                 term.write("!")
  299.             elseif isMine(x,y) then
  300.                 term.setBackgroundColor(colors.yellow)
  301.                 term.setTextColor(colors.red)
  302.                 term.write("X")
  303.                 term.setBackgroundColor(colors.green)
  304.                 term.setTextColor(colors.lime)
  305.             elseif viewport[getKey(x,y)] == EMPTY and isMineOrNumber(x,y) then
  306.                 term.setBackgroundColor(colors.gray)
  307.                 local number = isMineOrNumber(x,y)
  308.                 if number == 1 then
  309.                     term.setTextColor(colors.lightBlue)
  310.                 elseif number == 2 then
  311.                     term.setTextColor(colors.lime)
  312.                 elseif number == 3 then
  313.                     term.setTextColor(colors.yellow)
  314.                 elseif number == 4 then
  315.                     term.setTextColor(colors.orange)
  316.                 elseif number == 5 then
  317.                     term.setTextColor(colors.red)
  318.                 elseif number > 5 then
  319.                     term.setTextColor(colors.orange)
  320.                     term.setBackgroundColor(colors.red)
  321.                 end
  322.                 term.write(string.sub(tostring(isMineOrNumber(x,y)),1,1))
  323.             else
  324.                 term.setBackgroundColor(colors.black)
  325.                 term.write(" ")
  326.                 term.setBackgroundColor(colors.green)
  327.             end
  328.         end
  329.     end
  330. end
  331.  
  332. function game()
  333.     local tick = nil
  334.     while true do
  335.         local termWidth = term.getSize()
  336.         xOff = 0
  337.         if center then
  338.             xOff = math.ceil(termWidth / 2 - mapWidth / 2)
  339.             renderMap(xOff, 2)     
  340.         else
  341.             renderMap(0,2)
  342.         end
  343.         if not alive then
  344.             term.setBackgroundColor(colors.black)
  345.             term.setTextColor(colors.red)
  346.             if center then
  347.                 term.setCursorPos(math.ceil(termWidth / 2 - string.len("you dead") / 2), 2)
  348.             else
  349.                 term.setCursorPos(1,2)
  350.             end
  351.             term.write("KA BOOM!")
  352.             return true
  353.         elseif win then
  354.             term.setBackgroundColor(colors.black)
  355.             term.setTextColor(colors.red)
  356.             if not alive then
  357.                 term.setCursorPos(math.ceil(termWidth / 2 - string.len("you win") / 2), 2)
  358.             else
  359.                 term.setCursorPos(1,2)
  360.             end
  361.             term.write("VICTORY")
  362.             return true
  363.         end
  364.         term.setBackgroundColor(colors.black)
  365.         term.setTextColor(colors.white)
  366.         local tyme = "0"
  367.         if gameStarted then
  368.             tyme = tostring(math.floor(os.clock() - startTime))
  369.         end
  370.         term.setCursorPos(mapWidth + xOff - string.len(tyme) + 1, 1)
  371.         term.write( tyme)
  372.         local event = { os.pullEvent() }
  373.         if not gameStarted and event[1] == "mouse_click" then
  374.             gameStarted = true
  375.             startTime = os.clock()
  376.         end
  377.         if not tick then
  378.             tick = os.startTimer(0.5)
  379.         end
  380.         if event[1] == "mouse_click" then
  381.             x,y = event[3], event[4] -2
  382.             if center then
  383.                 x,y = event[3] - xOff, event[4] - 2
  384.             end
  385.             if event[2] == 1 and inBounds(x,y) then
  386.                 dig(x, y)
  387.             elseif event[2] == 2 then
  388.                 toggleFlag(x, y)
  389.             end
  390.         elseif event[1] == "timer" and event[2] == tick then
  391.             tick = os.startTimer(0.5)
  392.         elseif event[1] == "key" then
  393.             if event[2] == 59 then
  394.                 sDifficulty = "easy"
  395.                 return false
  396.             elseif event[2] == 60 then
  397.                 sDifficulty = "medium"
  398.                 return false
  399.             elseif event[2] == 61 then
  400.                 sDifficulty = "hard"
  401.                 return false
  402.             end
  403.         end
  404.     end
  405. end
  406.  
  407. function newGame()
  408.     term.clear()
  409.     term.setCursorPos(1,1)
  410.     createMapAtLevel(sDifficulty)
  411.     clearViewport()
  412.     gameStarted = false
  413.     --printMine(true)
  414.     win = false
  415.     alive = true
  416. end
  417.  
  418. while true do
  419.     newGame()
  420.     if game() then
  421.         sleep(3)
  422.     end
  423. end
Advertisement
Add Comment
Please, Sign In to add comment