Advertisement
GopherAtl

Minesweeper v1.8 (computercraft lua)

Jan 17th, 2013
7,742
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 14.03 KB | None | 0 0
  1. local displays={}
  2. local termOption
  3. if term.isColor() then
  4.   displays[1]={wrap=nil,side=nil,name="Terminal"}
  5.   termOption=true
  6. end
  7.  
  8. for _,side in pairs(rs.getSides()) do
  9.   if peripheral.getType(side)=="monitor" then
  10.     local wrap=peripheral.wrap(side)
  11.     if wrap.isColor() then
  12.       local disp={wrap=wrap,side=side, name="Monitor ("..side..")"}
  13.       displays[#displays+1]=disp
  14.     end
  15.   end
  16. end
  17.  
  18. function writeCentered(text,line,clearFirst,fg,bg,padChar)
  19.   local w=term.getSize()
  20.   fg=fg or colors.white
  21.   bg=bg or colors.black
  22.   local x=math.floor((w-#text)/2)
  23.   term.setTextColor(fg)
  24.   term.setBackgroundColor(bg)
  25.  
  26.   if padChar then
  27.      text=string.rep(padChar,x)..text
  28.      text=text..string.rep(padChar,w-#text)
  29.      term.setCursorPos(1,line)
  30.   else
  31.      term.setCursorPos(x+1,line)
  32.      if clearFirst then term.clearLine() end
  33.   end
  34.   term.write(text)
  35.   return x+1
  36. end
  37.  
  38. if #displays==0 then
  39.   print("Minesweep requires an advanced computer or monitor!")
  40.   return
  41. end
  42. local redirected=false
  43. local prevRedir
  44.  
  45. local function exitnow(forced)
  46.   if redirected then if term.restore then term.restore() else term.redirect(prevRedir) end end
  47.   term.setTextColor(colors.white)
  48.   term.setBackgroundColor(colors.black)
  49.   local w,h=term.getSize()
  50.   term.setCursorPos(1,h)
  51.   if forced then
  52.     print("terminated")
  53.     error()
  54.   else
  55.     term.scroll(1)
  56.     print("Thanks for playing!")
  57.   end
  58.    
  59. end
  60.  
  61. local function doSimpleMenu(title, list)
  62.   term.clear()
  63.   local selected=1
  64.   writeCentered(title,1)
  65.   local done=false
  66.   buttonPos={}
  67.   while not done do
  68.     for i=1,#list do
  69.       local text=list[i].name
  70.       local fg, bg
  71.       if i==selected then
  72.         fg=colors.black
  73.         bg=colors.white
  74.         text="["..text.."]"
  75.       else
  76.         fg=colors.white
  77.         bg=colors.black
  78.         text=" "..text.." "
  79.       end  
  80.       buttonPos[i]=writeCentered(text,4+i,false, fg,bg)+1
  81.     end
  82.  
  83.     while true do
  84.       local e={os.pullEventRaw()}
  85.       if e[1]=="terminate" then
  86.         exitnow(true)
  87.       elseif e[1]=="key" then
  88.         if e[2]==keys.up then
  89.           selected=selected-1
  90.           if selected==0 then selected=#list end
  91.           break
  92.         elseif e[2]==keys.down then
  93.           selected=selected+1
  94.           if selected>#list then selected=1 end
  95.           break
  96.         elseif e[2]==keys.enter then
  97.           done=true
  98.           break
  99.         end
  100.       elseif e[1]=="mouse_click" or e[1]=="monitor_touch" then
  101.         if e[4]>4 and e[4]<=4+#list then
  102.           local i=e[4]-4
  103.           if e[3]>=buttonPos[i] and e[3]<buttonPos[i]+#list[i].name then
  104.             selected=i
  105.             done=true
  106.             break
  107.           end
  108.         end
  109.       end          
  110.     end
  111.   end
  112.   return selected
  113. end
  114.  
  115. local activeDisplay=1
  116. if #displays>1 then
  117.   activeDisplay=doSimpleMenu("Select the display to play on:",displays)
  118. end
  119.  
  120. if displays[activeDisplay].wrap then
  121.   term.setTextColor(colors.white)
  122.   term.setBackgroundColor(colors.black)
  123.   term.setCursorPos(1,19)
  124.   term.scroll(1)
  125.   print("Playing Minesweep on "..displays[activeDisplay].side.." monitor...")  
  126.   prevRedir=term.redirect(displays[activeDisplay].wrap)
  127.   redirected=true
  128.   term.setTextColor(colors.white)
  129.   term.setBackgroundColor(colors.black)
  130.   term.clear()
  131.   displays[activeDisplay].wrap.setTextScale(.5)
  132. end
  133.  
  134. local difLevels={
  135.   { name="easy", width=6, height=6,mines=6},
  136.   { name="medium", width=15, height=8,mines=15},
  137.   { name="hard", width=30,height=17,mines=60},
  138. }
  139.  
  140. local w,h=term.getSize()
  141. if w<20 then
  142.   difLevels[3]=nil
  143. end
  144.  
  145. local level=doSimpleMenu("difficulty",difLevels)
  146. local level=difLevels[level]
  147.  
  148. local field={}
  149.  
  150. local function forEachValidNeighbor(x,y,func)
  151.   for y2=math.max(y-1,1),math.min(y+1,level.height) do
  152.     for x2=math.max(x-1,1),math.min(x+1,level.width) do
  153.       if x~=x2 or y~=y2 then
  154.         func(x2,y2)      
  155.       end
  156.     end
  157.   end
  158. end
  159.  
  160. local function genField()
  161.   for y=1,level.height do
  162.     field[y]={}
  163.     for x=1,level.width do
  164.       field[y][x]={mine=false,count=0,revealed=false,flag=false}
  165.     end
  166.   end
  167.  
  168.  
  169.   for i=1,level.mines do
  170.     local x, y
  171.     repeat
  172.       x,y=math.random(1,level.width),math.random(level.height)
  173.     until field[y][x].mine==false
  174.     field[y][x].mine=true
  175.     forEachValidNeighbor(x,y,function(x,y) field[y][x].count=field[y][x].count+1 end)
  176.   end
  177. end
  178.  
  179. local xoff,yoff=math.floor((w-level.width)/2), math.floor((h-level.height+2)/2)
  180.  
  181. local mineColor, mineColorBg=colors.black,colors.red
  182. local flagColor=colors.red
  183. local unrevealedColorB=colors.lightGray
  184. local flagWrong=colors.black
  185. local flagWrongBg=colors.red
  186. local screenBg=colors.gray
  187. local infoBarColor=colors.yellow
  188. local infoBarBG=colors.blue
  189. local winColor, winColorBG, loseColor, loseColorBG=colors.green,colors.lightGray, colors.red, colors.lightGray
  190.  
  191. if not term.isColor() then
  192.   mineColor, mineColorBg=colors.white,colors.black
  193.   flagColor=colors.black
  194.   unrevealedColorB=colors.white
  195.   flagWrong=colors.white
  196.   flagWrongBg=colors.black
  197.   screenBg=colors.black
  198.   infoBarColor=colors.white
  199.   infoBarBG=colors.black
  200.   winColor, winColorBG, loseColor, loseColorBG=colors.white, colors.black, colors.white, colors.black
  201. end
  202.  
  203. local numColors = {
  204.   colors.lightBlue,
  205.   colors.green,
  206.   colors.red,
  207.   colors.blue,
  208.   colors.magenta,
  209.   colors.cyan,
  210.   colors.pink,
  211.   colors.purple,
  212. }
  213.  
  214. local function getNumColor(n)
  215.   if n>0 and term.isColor() then
  216.     return numColors[n]
  217.   end
  218.   return colors.white
  219. end
  220.  
  221. local function drawTile(x,y)
  222.   term.setCursorPos(xoff+x,yoff+y)
  223.   if field[y][x].revealed then
  224.     if field[y][x].mine then
  225.       if field[y][x].flag then
  226.         term.setTextColor(mineColorBg)
  227.         term.setBackgroundColor(mineColor)
  228.       else
  229.         term.setTextColor(mineColor)
  230.         term.setBackgroundColor(mineColorBg)
  231.       end
  232.       term.write("X")
  233.     else
  234.       local n=field[y][x].count
  235.       if field[y][x].flag==true then
  236.         term.setBackgroundColor(flagWrongBg)
  237.         term.setTextColor(flagWrong)
  238.         term.write("?")
  239.       else
  240.         term.setTextColor(getNumColor(n))
  241.         term.setBackgroundColor(colors.black)
  242.         if n==0 then
  243.           term.write(" ")
  244.         else
  245.           term.write(string.char(string.byte("0")+field[y][x].count))
  246.         end
  247.       end
  248.     end
  249.   elseif field[y][x].flag==true then
  250.     term.setTextColor(flagColor)
  251.     term.setBackgroundColor(unrevealedColorB)
  252.     term.write("?")
  253.   else
  254.     term.setTextColor(colors.black)
  255.     term.setBackgroundColor(unrevealedColorB)
  256.     term.write(" ")
  257.   end
  258. end
  259.  
  260.  
  261.  
  262. function drawGrid()
  263.   for y=1,level.height do
  264.     for x=1,level.width do  
  265.       drawTile(x,y)
  266.     end
  267.   end
  268. end
  269.  
  270. local clickMode=1
  271. local gameover
  272. local revealToWin
  273.  
  274. local buttonOffset
  275.  
  276. local function drawButtons()
  277.   local text="new quit "
  278.   if clickMode==1 then
  279.     text=text.."flag"
  280.   else
  281.     text=text.."show"
  282.   end
  283.  
  284.   buttonOffset=writeCentered(text,1,true,infoBarColor,infoBarBG)
  285. end
  286.  
  287.  
  288. local function reveal(x,y)
  289.   local revealStack={{x,y}}
  290.   local visited={}
  291.   visited[x+y*level.width]=true
  292.   while #revealStack>0 do
  293.     x,y=unpack(table.remove(revealStack,1))
  294.    
  295.     if not field[y][x].revealed and not field[y][x].flag then
  296.       revealToWin=revealToWin-1
  297.       field[y][x].revealed=true
  298.       drawTile(x,y)
  299.       if field[y][x].count==0 then
  300.         forEachValidNeighbor(x,y,
  301.           function(x,y)
  302.             if not visited[x+y*level.width] and (field[y][x].revealed or field[y][x].flag)==false then
  303.               revealStack[#revealStack+1]={x,y}
  304.             end
  305.           end
  306.         )
  307.       end
  308.     end
  309.   end
  310. end
  311.  
  312. --declared here, so drawBar can see it
  313. local seconds=0
  314. local numMinesLeft=0
  315.  
  316. local function drawInfoBar(message,fg,bg)
  317.   fg=fg or infoBarColor
  318.   bg=bg or infoBarBG
  319.  
  320.   local text
  321.   if message then
  322.     text="["..message.."]"
  323.   else
  324.     text=string.format("[ %2d/%2d  %3ds ]",numMinesLeft,level.mines,seconds)
  325.   end
  326.  
  327.   writeCentered(text,2,false,fg,bg,"-")
  328.    
  329. end
  330.  
  331. local function confirm(text)
  332.   writeCentered(text,1,true,infoBarColor,infoBarBG)
  333.   local x=writeCentered("[ yes no ]",2,true,infoBarColor,infoBarBG,"-")+2
  334.   local res  
  335.   while true do
  336.     local e={os.pullEvent()}
  337.     if e[1]=="mouse_click" or e[1]=="monitor_touch" then
  338.       if e[4]==2 then
  339.         local cx=e[3]-x+1
  340.         if cx>0 and cx<4 then
  341.           res=true
  342.           break
  343.         elseif cx>4 and cx<7 then
  344.           res=false
  345.           break
  346.         end
  347.       end
  348.     end
  349.   end
  350.   drawButtons()
  351.   drawInfoBar()
  352.   return res
  353. end
  354.  
  355. --length of a real second, in units of os.time()
  356. local timeSecond=1/50
  357.  
  358. function runGame()
  359.   while true do
  360.     term.setTextColor(colors.white)
  361.     term.setBackgroundColor(screenBg)  
  362.     term.clear()
  363.     drawInfoBar()
  364.  
  365.     genField()
  366.     drawGrid()
  367.     revealToWin=level.width*level.height-level.mines
  368.     numMinesLeft=level.mines
  369.  
  370.     drawButtons()
  371.  
  372.     gameover=nil
  373.     local startTime=nil
  374.     local alarmTime=nil
  375.     local secondAlarm=nil
  376.     seconds=0
  377.  
  378.     while true do
  379.       local e={os.pullEventRaw()}
  380.       if e[1]=="terminate" then
  381.         exitnow(true)
  382.       elseif e[1]=="alarm" and e[2]==secondAlarm then
  383.         seconds=seconds+1
  384.         drawInfoBar()
  385.         alarmTime=(alarmTime+timeSecond)%24
  386.         secondAlarm=os.setAlarm(alarmTime)
  387.       elseif e[1]=="mouse_click" or e[1]=="monitor_touch" then
  388.         local clickModeX=clickMode
  389.  
  390.         if e[1]=="mouse_click" and e[2]~=1 then
  391.           --use button to determine action
  392.           clickModeX=e[2]
  393.         end
  394.  
  395.         --determine target tile
  396.         local x,y=e[3],e[4]
  397.         if x>xoff and x<=xoff+level.width and y>yoff and y<=yoff+level.height then
  398.           --clicked field!
  399.           x,y=x-xoff,y-yoff
  400.           local tile=field[y][x]
  401.           if clickModeX==1 then
  402.             --reveal, unless flagged?
  403.             if tile.revealed then
  404.               --count flags around
  405.               local count=0
  406.               forEachValidNeighbor(x,y,function(x,y) if not field[y][x].revealed and field[y][x].flag then count=count+1 end end)
  407.               if count==tile.count then
  408.                 forEachValidNeighbor(x,y,
  409.                   function(x,y)
  410.                     if field[y][x].revealed==false and field[y][x].flag==false then
  411.                       if field[y][x].mine then
  412.                         gameover="lose"
  413.                       else
  414.                         reveal(x,y)
  415.                       end
  416.                     drawTile(x,y)
  417.                     end
  418.                   end)
  419.                 if gameover then break end
  420.               end
  421.             elseif tile.flag==false then
  422.               if startTime==nil then
  423.                 startTime=os.time()
  424.                 alarmTime=startTime+timeSecond
  425.                 secondAlarm=os.setAlarm(alarmTime)
  426.               end
  427.               reveal(x,y)
  428.               if tile.mine then
  429.                 gameover="lose"
  430.                 break
  431.               end
  432.             end
  433.           elseif clickModeX==2 then
  434.             --toggle flag
  435.             if tile.revealed==false then
  436.               if tile.flag then
  437.                 numMinesLeft=numMinesLeft+1
  438.               else
  439.                 numMinesLeft=numMinesLeft-1
  440.               end
  441.               drawInfoBar()
  442.               tile.flag=not tile.flag
  443.               drawTile(x,y)          
  444.             else
  445.               --flag all tiles if the surrounding unrevealed count equals mine count
  446.               local count=0
  447.               forEachValidNeighbor(x,y,
  448.                 function(x,y)
  449.                   if not field[y][x].revealed then
  450.                     count=count+1
  451.                   end
  452.                 end)
  453.               if count==field[y][x].count then
  454.                 forEachValidNeighbor(x,y,
  455.                   function(x,y)
  456.                     if not field[y][x].revealed and not field[y][x].flag then
  457.                       field[y][x].flag=true
  458.                       numMinesLeft=numMinesLeft-1
  459.                       drawTile(x,y)
  460.                     end
  461.                   end)
  462.                 drawInfoBar()
  463.               end
  464.             end          
  465.           end
  466.         else
  467.           --not a tile, check menu
  468.           if y==1 then
  469.             x=x-buttonOffset+1
  470.             if x>0 and x<4 then
  471.               --new
  472.               if confirm("Start new game?") then
  473.                 gameover="new"
  474.                 break
  475.               end
  476.             elseif x>4 and x<9 then
  477.               --quit
  478.               if confirm("Exit minesweep?") then
  479.                 gameover="quit"
  480.                 break
  481.               end
  482.             elseif x>9 and x<14 then
  483.               clickMode=clickMode==1 and 2 or 1
  484.               drawButtons()
  485.             end
  486.           end
  487.         end
  488.       else
  489.         --any other event
  490.       end
  491.       if revealToWin==0 then
  492.         gameover="win"
  493.         break
  494.       end
  495.     end
  496.     term.setTextColor(colors.white)
  497.     term.setBackgroundColor(colors.black)
  498.     if gameover=="quit" then
  499.       break
  500.     end
  501.     if gameover~="new" then
  502.  
  503.       drawInfoBar("You "..gameover.."!",gameover=="win" and winColor or loseColor, gameover=="win" and winColorBG or loseColorBG)
  504.       term.setTextColor(colors.white)
  505.       for y=1,level.height do
  506.         for x=1,level.width do
  507.           if field[y][x].mine and not field[y][x].revealed then
  508.             field[y][x].revealed=true
  509.             drawTile(x,y)
  510.           elseif field[y][x].mine==false and field[y][x].flag then
  511.             field[y][x].revealed=true
  512.             drawTile(x,y)
  513.           end
  514.         end
  515.       end
  516.  
  517.       os.startTimer(5)
  518.       while ({os.pullEvent()})[1]=="alarm" do
  519.         --skip alarm so it doesn't override within a second from the game timer events
  520.       end
  521.  
  522.       drawInfoBar()
  523.  
  524.       if not confirm("Play Again?") then
  525.         break
  526.       end
  527.     end  
  528.   end
  529. end
  530.  
  531. local ok, err=pcall(runGame)
  532.  
  533. term.setTextColor(colors.white)
  534. term.setBackgroundColor(colors.black)
  535. term.clear()
  536.  
  537. if not ok then
  538.   print(err)
  539. end
  540.  
  541. exitnow()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement