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

String manipulation question

Started by stringcheese, 04 September 2013 - 04:43 PM
stringcheese #1
Posted 04 September 2013 - 06:43 PM
Hello, I spent today learning Lua in order to write a program that would manage a list of tasks and display them one at a time on an advanced monitor,e.g., "Build an oil refinery!" "Breed industrious bees!" The only part I've had trouble with is formatting the string for display on the monitor so that the lines break naturally. It's much more complicated than I thought it would be and I'm stumped!

The advanced monitor is three blocks wide and two blocks tall. The text scale on the monitor is set to 1.5. The top text row is taken by a title bar and the bottom three text rows taken by buttons, so I want to be able to display a task in the four intermediate text rows. I'm storing the task as a particular string in an array of strings (let's say, taskList[currentTask]). I was hoping someone could help me write a function that writes the string on the four lines so that the lines break between words (the lines are 19 columns long except for the first, which has three initial characters for the task number, a period, and a space so only 16 columns for the task). I'm having trouble figuring out Lua's string library, so would be very grateful for any ideas on how to do this!
Lyqyd #2
Posted 04 September 2013 - 08:15 PM
Split into new topic.

Please post your current code so that we can help you improve/fix it.
stringcheese #3
Posted 04 September 2013 - 08:59 PM
I guess the problem is that I have no idea, even after looking at the Lua string library, how to make an algorithm to do this. I want to take any string and be able to print it on four lines of 19 columns each with line breaks between words so that words are not cut in half between two lines.

For example, I want the algorithm to read a string, say, "Build an automated cow farm to stockpile leather," and instead of displaying on the monitor:

1. Build an automat
ed cow farm to stoc
kpile leather

I want it to display the string as:

1. Build an
automated cow farm
to stockpile
leather


I'm not sure really how to even start with this particular function, so I don't have any code. I guess it's a conceptual problem, not a code editing problem?
Lyqyd #4
Posted 04 September 2013 - 09:41 PM
Well, best to start by figuring out what you want the algorithm to do to the string, before you even look at what it'll take to code it. Something like:


if the string is longer than the line then
  find the first word that is broken
  start a new line before that word
  repeat with a new string starting at the word that would have broken
else
  print the string and return
end

Start with a vague outline of what you want to do and bear down on each piece of it until you're putting it into code, if that makes sense.
stringcheese #5
Posted 05 September 2013 - 02:08 AM
This may have been a silly question, but I guess since my only programming experience is using a little QBasic 20 years ago, I don't really think algorithmically. Anyway, I worked something out by starting with your response and slowly improving it (now it's > 100 lines). Thanks!
BlankWolf #6
Posted 05 September 2013 - 03:25 AM
Well I hade done somthing similar.
Thats an example how you can do that.

str = "Build an automated cow farm to stockpile leather" -- the string you will print to the screen
termX, termY = term.getSize()
write('1. ')
for word in string.gmatch(x, "%S+") do -- that will give you word by word from the string (wihtout a space)
  x, y = term.getCursorPos()
  if (#word + x > termX) then -- checks if the word will fit to the screen
	print() -- when not go to the next line
  end
  write(word) -- write the word
  if (#word + x < termX) then -- checks if there is space for a space
	write(' ') -- write the space
  end
end

I can't test the code. So sorry if there is any syntax error ^_^/>
LordIkol #7
Posted 05 September 2013 - 04:27 AM
Hi together,

Well I hade done somthing similar.
Thats an example how you can do that.

str = "Build an automated cow farm to stockpile leather" -- the string you will print to the screen
termX, termY = term.getSize()
write('1. ')
for word in string.gmatch(x, "%S+") do -- that will give you word by word from the string (wihtout a space)
  x, y = term.getCursorPos()
  if (#word + x > termX) then -- checks if the word will fit to the screen
	print() -- when not go to the next line
  end
  write(word) -- write the word
  if (#word + x < termX) then -- checks if there is space for a space
	write(' ') -- write the space
  end
end

I can't test the code. So sorry if there is any syntax error ^_^/>/>

im not sure if print works with a monitor. And i think the term.getsize() does not include the textsize parameter. but im not sure about that will test it when im home.
When my suggestion is correct then you can use term.redirect but with that i guess you can not use textscale.

I would create an own print function with a maxchar Limit. just check how much chars you can write per line then use the print function below.
it should do the job but i didnt test it. you have to switch term.xxx to the variable for your Monitor peripheral.

if you want some explenation for the code just ask then i put some comments in.


local sm = {"I hate Cats","Coding HTML for Food","The Internet is for Stuff","Minecraft is a little boring when you not have Computercraft installed"}

local function splistring(x)
local c ={}
local i=1
for word in string.gmatch(x, "[%a%d]+") do
--if tonumber(word) == nil then
c[i]= word
--else
--c[i] = tonumber(word)
--end
i=i+1
end
return c
end

local function newprint(line, row, str, mchars)
local actline = line
if #str > mchars then
local mytext = splistring(str)
local newtext = mytext[1].." "
for i = 2,#mytext do
if #newtext+#mytext[i] < mchars then
newtext = newtext..mytext[i].." "
else
term.setCursorPos(row,actline)
term.write(newtext)
actline = actline+1
newtext = mytext[i].." "
end
end
term.setCursorPos(row,actline)
term.write(newtext)
else
term.setCursorPos(row,actline)
term.write(str)
end
end

term.clear()
newprint(1,2,sm[3],15)

Edit by Bubba: Keep it PG please.
BigTwisty #8
Posted 05 September 2013 - 01:11 PM

for word in string.gmatch(x, "%S+") do -- that will give you word by word from the string (wihtout a space)
  x, y = term.getCursorPos()
  if (#word + x > termX) then -- checks if the word will fit to the screen
	print() -- when not go to the next line
  end
  write(word) -- write the word
  if (#word + x < termX) then -- checks if there is space for a space
	write(' ') -- write the space
  end
end

This code is good, but it doesn't handle certain corner cases:
  • Multiple spaces between words
  • Words that are longer than the screen width
You might try this:


function printWrap(text)
  local width, height = term.getSize()
  local x, y
  local function newLine()
    x, y = 1, y + 1
    if y > height then
      y = height
      term.scroll()
    end
    term.setCursorPos(x, y)
  end
  for space,word in text:gmatch("(%s-)(%S+)") do
    fullWord = space..word
    x, y = term.getCursorPos()
    if x + #fullWord > width then
      if #w > width then -- Handle words longer than screen width
        while #fullWord > width do
          write(fullWord:sub(1, width - x))
          fullWord = fullWord:sub(width - x + 1)
          newLine()
          term.setCursorPos(x, y)
        end
      else
        -- handle wrapped words shorter than screen width
        newLine()
        term.setCursorPos(x, y)
        write(w)
      end
    else
      write(fullWord)
    end
  end
end

-snip-

Oh, the lack of tabs!!! The pain!!!
BlankWolf #9
Posted 06 September 2013 - 01:38 AM
This code is good, but it doesn't handle certain corner cases:
  • Multiple spaces between words
  • Words that are longer than the screen width

Well thats true. But I said:
Thats an example how you can do that.
Lyqyd #10
Posted 06 September 2013 - 01:57 AM
It isn't an example of how he could do that though, because of those exceptions. Your answer has been shown to be inferior. The correct response is to accept the better answer, thank the poster for the correction if you desire, and move on. Ask a Pro is all about the quality of the answers, so attempting to justify a less useful answer isn't helpful. :)/>
LordIkol #11
Posted 06 September 2013 - 06:47 AM
Edit by Bubba: Keep it PG please.

First i was like Whaat???!! did i use bad language, then i found the "Stuff" :D/>
Sorry Bubba will watch for that next time.

-Snip-

Nice code too :)/>
- you might want to add a number to the term.scroll() else it gives an error when he trys to make a new line.
- if the text has a single word that is longer then the screen it will not be printed. (if its a feature it should announce that this will happen ;)/> )

And when i checked my own piece of code i found a problem with numbers in the text. (fixed it)
its because i wrote the splitstring function for an other programm where i need numbers to be numbers)

It isn't an example of how he could do that though, because of those exceptions. Your answer has been shown to be inferior. The correct response is to accept the better answer, thank the poster for the correction if you desire, and move on. Ask a Pro is all about the quality of the answers, so attempting to justify a less useful answer isn't helpful. :)/>

Well spoken, im far away from beeing a Pro, but i try to help the people with their problems. And im always Happy if someone (mostly Bit) is correcting my mistakes or post a solution that makes my solution look toally Ugly :D/>
The reason is that i can learn something and the person who started the Topic learns something too, so its a Win/Win Situation.

greets
Loki

Edit: Changed something :D/>
Lyqyd #12
Posted 06 September 2013 - 12:45 PM
If we're trying to write to only a subset of the screen, term.scroll is good to avoid, as it changes the contents of the whole screen. "sth." doesn't stand for anything. Perhaps you intended to write "something"? Please use full words for clarity. This is a forum, not an instant messaging service.