LoginLogin
Nintendo shutting down 3DS + Wii U online services, see our post

How to check for certain phrases within a string?

Root / Programming Questions / [.]

ResetReloadCreated:
Greetings. (I'm on mobile so apologies for the formatting.) I'm trying to make it so if my program notices a keyword, command, or user-defined function after inputting something, it will seperate that keyword from the rest of the string and execute it instead of cherrypicking every way the player could input something. Example: If STRING$=="Walk over to John" then WALKTO If STRING$=="Goto John" then WALKTO It results in the same outcome, but it seems like a waste of time to do this for every interaction.

use INSTR
position=INSTR("string","search")
it returns the position of the match (starting at 0) or -1 if it couldn't find it.

use INSTR
position=INSTR("string","search")
it returns the position of the match (starting at 0) or -1 if it couldn't find it.
Sorry, maybe I explained this wrong or maybe I'm failing to understand how INSTR fully works. I want to make a list of synonyms which my program will recognize as a single command, like how hello, hi, and howdy mean the same thing. INSTR seems to only tell me at which point in the string it notices a word, which will be useful, but it's not what I need at the moment.

'Splits a string based on the given delimiter
DEF SPLITSTRING$(STRING$,DELIM$)
 DIM SPLIT$[0]
 DIM NEXT%=0
 WHILE LEN(STRING$)>0 'Continue until the whole string is consumed
  NEXT%=INSTR(STRING$,DELIM$) 'Find the next occurrence of the delimiter
  IF NEXT%<0 THEN PUSH SPLIT$,STRING$:BREAK 'If there are no more delimiters, put the rest in the split and get out.
  IF NEXT%>0 THEN PUSH SPLIT$,LEFT$(STRING$,NEXT%) 'If the first character is not a delimiter, put everything up to the delimiter in the array
  STRING$=RIGHT$(STRING$,LEN(STRING$)-NEXT%-1) 'Remove the word, including the delimiter
 WEND
 RETURN SPLIT$
END

'Determines if the given string has any of the given commands(if the commands are in a string separated by spaces)
DEF HASCOMMAND(STRING$,COMMANDS$)
 DIM SCOMMANDS$[0],I
 SCOMMANDS$=SPLITSTRING(COMMANDS$," ") 'Split COMMAND into an array
 FOR I=0 TO LEN(SCOMMANDS$)-1
  IF INSTR(STRING$,SCOMMANDS$[I])>=0 THEN RETURN TRUE 'Use INSTR to see if string has command
 NEXT
 RETURN FALSE
END

IF HASCOMMAND("WALK OVER TO JOHN","WALK GOTO MOVE TRAVEL")>=0 THEN
 'Did a walk command
ENDIF
Sometimes you have to write more code now to write less code later. If you're not dead-set on having the commands all be in a string, you can do away with the string-split function and just do:
DEF HASCOMMAND(STRING$,COMMANDS$)
 DIM I
 FOR I=0 TO LEN(COMMANDS$)-1
  IF INSTR(STRING$,COMMANDS$[I])>=0 THEN RETURN TRUE 'Use INSTR to see if string has command
 NEXT
 RETURN FALSE
END

DIM WALKCOMMANDS$[0]
PUSH WALKCOMMANDS$,"WALK"
PUSH WALKCOMMANDS$,"GOTO"
PUSH WALKCOMMANDS$,"MOVE"
PUSH WALKCOMMANDS$,"TRAVEL"

IF HASCOMMAND("WALK OVER TO JOHN", WALKCOMMANDS$) THEN
 'Had a walk command
ENDIF
Doing it the latter way will result in more code down the road though, as all those PUSH commands really start to add up when compared to just writing STRINGSPLIT once.

Thank you. I'll try both of them.

Thank you. I'll try both of them.
No wait, just pick one lol. They both do the same thing, and those functions will interfere with each other. Also, I didn't test either of them >_<. Edit: I would definitely pick the first one, especially if you're going to have a lot of commands. Also, try to understand the code as you use it, as it'll help you develop your own code in the future.

No wait, just pick one lol. They both do the same thing, and those functions will interfere with each other. Also, I didn't test either of them >_<. Edit: I would definitely pick the first one, especially if you're going to have a lot of commands. Also, try to understand the code as you use it, as it'll help you develop your own code in the future.
Oh, I meant that I'll try them in separate programs, but the first one does seem better for the reasons you mentioned. Thank you. :) Edit: I'll tell you how they work later.

I'll tell you how they work later.
Cool, I hope they work out!

use INSTR
position=INSTR("string","search")
it returns the position of the match (starting at 0) or -1 if it couldn't find it.
I think he meant to give more context on this. Even though it appears the problem is already solved, I can't not do this if you know what I mean. Using INSTR, you can see not just when the string appears, but if. Using this, you can check for each synonym for the word and then send the process to a label, where you can check for more strings. Basically, you can check if there are any synonyms to Goto in the string, then go to @MOVE if so. @MOVE would contain the code that gets the name of the place/person the player wants to goto.

That's definitely not the *most* efficient string split function, but I don't think saving 0.00001 seconds will matter lol

That's definitely not the *most* efficient string split function, but I don't think saving 0.00001 seconds will matter lol
Clarity is almost always more important than speed (as you implied lol). Critical code with tight speed requirements is usually the only place you should worry about micro optimizations. As long as your algorithm isn't like... O(N!) complexity, you're usually fine :^)

I'll tell you how they work later.
Cool, I hope they work out!
I just tested the first method, and two things I've noticed are: The parenthesises after both PUSHes causes an error, so I took those out. No matter what I INPUT, the program still executes the user-defined function. I'm not sure if I missed something when writing your code or if it's something else. I'm going to test the second method. Thank you all for helping me out, by the way. :) Just finished the second one, and I ended up with the same results. If it's any help, here's the code with both methods: ESDQ83ZJ

my split function but don't use this
COMMON DEF SPLIT$(STRING$,SEPARATOR$)
 DIM RETURN$[0]
 VAR POS%=0
 REPEAT
  VAR NEXT%=INSTR(POS%,STRING$,SEPARATOR$)
  IF NEXT%==-1 THEN BREAK
  PUSH RETURN$,MID$(STRING$,POS%,NEXT%-POS%)
  POS%=NEXT%+LEN(SEPARATOR$)
 UNTIL FALSE
 PUSH RETURN$,RIGHT$(STRING$,LEN(STRING$)-POS%)
 RETURN RETURN$
END

my split function but don't use this
COMMON DEF SPLIT$(STRING$,SEPARATOR$)
 DIM RETURN$[0]
 VAR POS%=0
 REPEAT
  VAR NEXT%=INSTR(POS%,STRING$,SEPARATOR$)
  IF NEXT%==-1 THEN BREAK
  PUSH RETURN$,MID$(STRING$,POS%,NEXT%-POS%)
  POS%=NEXT%+LEN(SEPARATOR$)
 UNTIL FALSE
 PUSH RETURN$,RIGHT$(STRING$,LEN(STRING$)-POS%)
 RETURN RETURN$
END
Because of how SB operates on strings, I think your version is actually MUCH faster. Your version is the one I ended up using in my library lol.

my split function but don't use this
COMMON DEF SPLIT$(STRING$,SEPARATOR$)
 DIM RETURN$[0]
 VAR POS%=0
 REPEAT
  VAR NEXT%=INSTR(POS%,STRING$,SEPARATOR$)
  IF NEXT%==-1 THEN BREAK
  PUSH RETURN$,MID$(STRING$,POS%,NEXT%-POS%)
  POS%=NEXT%+LEN(SEPARATOR$)
 UNTIL FALSE
 PUSH RETURN$,RIGHT$(STRING$,LEN(STRING$)-POS%)
 RETURN RETURN$
END
Because of how SB operates on strings, I think your version is actually MUCH faster. Your version is the one I ended up using in my library lol.
It's actually only a tiny bit faster (496 ms per 10000 vs 551 ms) Either one is much better than the lazy way of doing it; just checking 1 character at a time and not using INSTR. (that one got 1174 ms per 10000)

Another alternative would be to use regex or LEX and make an actual "grammar" (with very few verbs) Ninjaedit: The advantage to this is it gives you a declarative way to control the ordering of words as well as having multiple ways to say the same thing. For example:
{movement} to {noun}
can be one case of input, where {movement} would be a list of possible verbs and {noun} is a pattern that names can take (alpha characters?) You could make lots of great sentences:
{use} {noun} {target} {noun}
{combine} {noun} {conjunction} {noun}
{look} ({noun}|{preposition} {noun}|{direction})
{movement} ({direction}|{noun})

Another alternative would be to use regex or LEX and make an actual "grammar" (with very few verbs)
Sorry for the late response, but is there an English translation for this? ^^'

Sorry for the late response, but is there an English translation for this? ^^'
I've always just used Google Translate on the page. Some of the phrasing can come across confusing because of this, but the actual code you would end up making is just regex. If you've never written a regular expression before it might be good to read up on them After my original post I was really gung-ho on pointing you at LEX and making a GRAMMAR.L file. This allows you have a really clean code, but doesn't actually get you any information about things like: "What noun was used?". For that you need REGEX capture groups... And that means a little bit more code... I actually just wrote this up and published it for simplicity's sake My little bit of code lets you write stuff like:
  TextyAdd "({MOVE}) to ({NOUN})", "moveTo"
  DEF moveTo T$[]
    ... T$[0] is your move command ...
    ... T$[1] is the noun ...
  END
  
Honestly, I'm all up for adding more features to this now... And just to cover the old idea:
What a lex file could be (sigh)
%option case-insensitive
%{
  'You don't have to put your functions here, but its kinda nice if you want one file
  DEF walkTo noun$
    'DO stuff with noun
  END
%}
DIGIT [0-9]
LETTER [A-Z]
SPACE [ ]+

NOUN {LETTER}+
MOVEMENT WALK|GOTO|MOVE|TRAVEL

%YY_DECL DEF interpret S$: VAR SP, EP
%%
{MOVEMENT}{SPACE}TO{SPACE}{NOUN} walkTo cap1$ '<-- this doesn't exist sadly
%%