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

Program suddenly ends

Started by stringcheese, 27 September 2014 - 06:56 PM
stringcheese #1
Posted 27 September 2014 - 08:56 PM
I am relatively new to programming, so this problem has an easy solution, I'm sure. I'm making a task list program that displays the current task on an attached monitor and lets you cycle through the tasks by clicking a button. I was tinkering with the program and suddenly it quits after running the main loop once and I can't figure out why. Nothing I added would seem to cause it to do that. If anyone is willing to look at the code and see if they can figure out why, I'd be very grateful.


-- Task List program
-- Requires an advanced monitor attached to the right side. Keeps a task list. Displays one task at a time on the monitor. Can click
-- "Next" on the monitor to cycle to the next task or "Done" to mark that task as done and delete it from the list. On the computer
-- terminal, can type in a new task or quit the program.

-- Global Variables:
-- currentTask
-- taskList
-- runNextLoop
-- monitor

-- Return the number of the last task in the list
function LastTask()
  local iteration = 1
  while taskList[iteration] ~= nil do
	iteration = iteration + 1
  end
  return iteration
end

-- Check for and attach monitor
function AttachMonitor()
  monitor = peripheral.wrap("right")
  return true
end

-- Check for and attach disk drive (not implemented yet)
-- Load task list from disk drive (not implemented yet)

-- Display menu on computer terminal
function DrawComputerMenu()
  -- Computer terminal screen is 51 x 19
  term.clear()
  term.setCursorPos(1, 2)
  write("***************************************************")
  write("*				Jerry's Task List				*")
  write("***************************************************")
  write("\n\n")
  write("A)dd a task\n")
  write("S)ave the task list\n")
  write("Q)uit this program\n")
  write("\n")
  write("Please select a menu choice.")
end

-- Draw the banner at the top of the monitor that says "CURRENT JOB"
function DrawBanner()
  monitor.setCursorPos(1, 1)
  monitor.setBackgroundColor(colors.red)
  monitor.write("	CURRENT JOB	")
  monitor.setBackgroundColor(colors.black)
end

-- Draw the buttons at the bottom of the monitor that say "NEXT" and "DONE"
function DrawButtons()
  monitor.setBackgroundColor(colors.green)
  monitor.setCursorPos(2, 8)
  monitor.write(" NEXT ")
  monitor.setCursorPos(13, 8)
  monitor.write(" DONE ")
  monitor.setBackgroundColor(colors.black)
end

-- Write the current task in the center of the monitor
function DrawTask()
  monitor.setCursorPos(2, 3)
  monitor.write(taskList[currentTask])
end

-- Refresh the monitor with banner, task, and buttons
function DrawMonitor()
  -- Monitor screen at 1.5 text scale is 19 x 8
  monitor.setTextScale(1.5)
  DrawBanner()
  DrawButtons()
  DrawTask()
end

-- Add task
function AddTask()
  currentTask = lastTask()
  term.setCursorPos(1, 12)
  write("Please type in the task you wish to add.")
  term.setCursorPos(1, 14)
  taskList[currentTask] = read()
  term.setCursorPos(1, 16)
  write("Got it! The task was added to the end of the list!")
  os.sleep(2)
end

-- Save task list
function SaveTaskList()
-- Not implemented yet
end

-- Delete task from list
function DeleteTask()
  local endOfTasks = LastTask()
  for i = currentTask, endOfTasks do
	taskList[i] = taskList[i + 1]
  end
  taskList[endOfTasks] = nil
  currentTask = currentTask - 1
  if currentTask < 1 then
	currentTask = 1
  end
end

runNextLoop = true

local isMonitorAttached = AttachMonitor()
if isMonitorAttached == false then
  print("Please ensure that an advanced monitor is attached!")
  runNextLoop = false
end

taskList = {}
currentTask = 1

-- Main loop
while runNextLoop == true do
  DrawComputerMenu()
  DrawMonitor()

  local event, param1, param2, param3 = os.pullEvent()
  if event == "key" then
	if param1 == 30 then
	  AddTask()
	elseif param1 == 31 then
	  SaveTaskList()
	elseif param1 == 16 then
	  runNextLoop = false
	else
	  term.setCursorPos(1, 12)
	  write("Please press a key corresponding to a menu choice or click a monitor button!")
	  os.sleep(2)
	end
  elseif (event == "mouse_click") and (param1 == 1) then
	if (param2 == 8) then
	  if (param3 > 1) and (param3 < 8) then
		currentTask = currentTask + 1
		if currentTask > LastTask() then currentTask = 1 end
	  elseif (param3 > 12) and (param3 < 19) then
		DeleteTask()
	  end
	end
  end

end
Dragon53535 #2
Posted 27 September 2014 - 09:22 PM
Been testing your code, i used pcall(DrawComputerMenu) and the same for DrawMonitor that way it doesn't error out, i'll find the reason for that soon. Line 81, you're calling lastTask(), however you've defined that as LastTask so you need to fix that. Any time I press a key, it automatically types it whenever i'm using that for adding a task, it prints a. Doing some testing it's your DrawMonitor function that errors.

Edit: drawtask errors. Error is that it's trying to write nil and for some reason just cleanly exits. Add this statement inside DrawTask

if taskList[currentTask] then -- If it exists.
  monitor.write(taskList[currentTask])
end
Doing so i believe fixed my printing a inside the add task.


Little tip, always think logically, if it errors before you can try to press a key, then something before that bugged, if it draw's the monitor, then that's where the error is since drawing the terminal didn't error :P/>
Also you might want to change your line that says "Jerry's Task List" since using write word wraps it so that it appears on 2 lines and doesn't look clean. I fixed it on my end so you can copy paste from me

write("*			    Jerry's Task List			    *")
Edited on 27 September 2014 - 07:36 PM
stringcheese #3
Posted 27 September 2014 - 09:47 PM
Thank you, that was so helpful! It's obviously a work in progress, but I don't think I would've figured that out on my own since there's no error message for trying to write a nil value.
Dragon53535 #4
Posted 27 September 2014 - 09:50 PM
I understand fully, it confused me greatly. A helpful tactic is to put prints at different parts, and if one doesn't happen, then the error was right before it. I did that for the drawmonitor, and then just went through until i found the spot that stopped it.
stringcheese #5
Posted 27 September 2014 - 09:51 PM
Speaking of, how might I make it so that a os.pullEvent asking for a key press doesn't then put the letter in the subsequent prompt?
Dragon53535 #6
Posted 27 September 2014 - 09:57 PM
I'm assuming by that statement that it does on your end, around the start of Addtask, i put in a sleep(1) which seemed to fix that for adding a task, since you're making everything happen in the same second it might put it in the buffer and never erase it, i'm going to go back in and test help you :P/>

Edit: yeah on my end adding a sleep(1) seems to of done the trick. Just tested it a bit more and you can actually just use sleep(0) and it will work.

Oh and os.sleep and just sleep are the same time, personally i find it easier to type sleep rather than os.sleep
Edited on 27 September 2014 - 08:01 PM
Bomb Bloke #7
Posted 28 September 2014 - 02:07 AM
Sleeping will do the trick under most circumstances. Sometimes, if there are other events floating around that you need to catch, it's best to avoid it. In this case I doubt it matters so much.

The idea is that pressing a character key generates two events - the first is a "key" event, the second is a "char" event. If you pull the first and ignore the second, then the "char" event remains in the queue. You can specifically pull it out like in this example:

while true do
  event, par = os.pullEvent()

  if event == "key" and par == keys.q then
    print("Exiting...")
    os.pullEvent("char")
    error()
  end
end

Sleeping (along with quite a few other functions - anything that pauses for any reason, basically) will empty out whatever's in the event queue (a timer event gets stuck on end, then all other events get pulled out and discarded until that timer event is reached).