Deleting specific letters in a string.

Hello. I have a question for you guys. I am currently making a game that's sort of like a word find. (You know the jumble of letters, and you try to find words hidden inside?) Well I have 99% of it done, but there's one very big problem with it. Let me explain the basic idea first. There's a 10x10 grid of randomly created tiles that all have letters on them. You click these tiles, and it spells out the word you're "typing" along the top of the screen. If you deselect a tile by clicking it again, the last letter in the word you're spelling gets deleted. But if you click one of the tiles from the middle of the word you're spelling out, it still just deletes the last letter, and not the letter corresponding to the tile. This actually makes it possible in my game to cheat easily. Let's say you're spelling "sassy", just because it has a bunch of S's in it. On the board, there is only 2 S's in the whole thing. You can unselect one of them, (erasing the last letter you "typed") reselect the letter you just lost, and use the unselected S again in the word. Which completely defeats the point of the game. So I need a way to erase a specific letter, when that specific letter's tile is deselected, and have it erased from the string.

Just so you know, I'm not exactly new to GML, but I can for sure say I am not great at it. I know for loops, ds grids and arrays are probably the answer to my problem, but I just don't grasp the ideas behind them, so I thought of a different way that seemed like it would work at the time I wrote the script. I thought, what if I checked the width of the string, made sure it was between range 1 and range 2, and if it is, delete the letter from the string using string_delete(); and the correct position of the letter I want gone. In my head, this should have 100% worked, in reality it 100% did not. In fact, it just deletes the entire string, which I could have done far easier than writing a script for it. lol

Below is a screenshot of the code I wrote. Can someone please explain 1) why the hell does this script not work the way I think it should, and 2) can you please give me some pointers or something about how to solve this problem, because I'm tired... I'm so tired.
 

Attachments

TsukaYuriko

☄️
Forum Staff
Moderator
Please post code as code, not as screenshots. There's no reason to do the latter, but all the reasons to do the former. We can't copypaste images into our code editors. ;)
 
ok. It's just a script, so without the rest of the code I didn't figure it would matter, but fair enough...


GML:
// Script assets have changed for v2.3.0 see
// https://help.yoyogames.com/hc/en-us/articles/360005277377 for more information
function scr_letterPos(){
global.pos=string_delete(global.message,0,0);

if string_width(global.message)>=0 && string_width(global.message)<=32{
    global.pos=string_delete(global.message,1,1);   
}
if string_width(global.message)>=33 && string_width(global.message)<=65{
    global.pos=string_delete(global.message,2,1);   
}
if string_width(global.message)>=66 && string_width(global.message)<=97{
    global.pos=string_delete(global.message,3,1);   
}
if string_width(global.message)>=98 && string_width(global.message)<=129{
    global.pos=string_delete(global.message,4,1);   
}
if string_width(global.message)>=130 && string_width(global.message)<=161{
    global.pos=string_delete(global.message,5,1);   
}
if string_width(global.message)>=162 && string_width(global.message)<=193{
    global.pos=string_delete(global.message,6,1);   
}
if string_width(global.message)>=194 && string_width(global.message)<=225{
    global.pos=string_delete(global.message,7,1);   
}
if string_width(global.message)>=226 && string_width(global.message)<=257{
    global.pos=string_delete(global.message,8,1);   
}
if string_width(global.message)>=258 && string_width(global.message)<=289{
    global.pos=string_delete(global.message,9,1);   
}
if string_width(global.message)>=290 && string_width(global.message)<=321{
    global.pos=string_delete(global.message,10,1);   
}
if string_width(global.message)>=322 && string_width(global.message)<=353{
    global.pos=string_delete(global.message,11,1);   
}
if string_width(global.message)>=354 && string_width(global.message)<=385{
    global.pos=string_delete(global.message,12,1);   
}
if string_width(global.message)>=386 && string_width(global.message)<=417{
    global.pos=string_delete(global.message,13,1);   
}
if string_width(global.message)>=418 && string_width(global.message)<=449{
    global.pos=string_delete(global.message,14,1);   
}
if string_width(global.message)>=450 && string_width(global.message)<=481{
    global.pos=string_delete(global.message,15,1);   
}

}
 

Alice

Toolmaker of Bucuresti
Forum Staff
Moderator
I have no idea what the script in the screenshot is supposed to do, so I can't comment on that.

What I would do instead is use a friendly ds_list structure, containing unique identifiers of specific tiles.
They can correspond to tile instances ids, or maybe to indices in a grid (0-9 for first row, 10-19 for second row and so on); it depends on how you implemented the grid in the first place. The important part is that:
- they are unique
- you can easily obtain the tile id given the tile
- you can easily obtain the tile letter given tile id

The general idea is that:
1. When you select a tile, you add that tile identifier to the list:
GML:
ds_list_add(selected_tiles, _tile_id);[code]

2. When you deselect a tile, you remove that identifier from the list:
[code=gml]ds_list_delete(selected_tiles, ds_list_find_index(selected_tiles, _tile_id));
3. After selection changes, you rebuild the string from the selected tiles, in the order they've been last selected:
GML:
selected_text = "";
for (var i = 0; i < ds_list_size(selected_tiles); i++) {
  var _tile_id = selected_tiles[| i];
  selected_text += _tile_id.letter;
}
Generally, I wrote that code with assumption that the identifiers are tile instances ids (rather than e.g. grid indices). Hopefully it won't be a problem for you to adjust the code to your needs.
 

Nidoking

Member
what if I checked the width of the string, made sure it was between range 1 and range 2, and if it is, delete the letter from the string using string_delete(); and the correct position of the letter I want gone
So... you're looking at the width, and thus effectively the length, of the string. The entire string. And using that to delete a letter. At what point in this idea does the position of the chosen character in the string come into it? Surely, you must at least understand that in order for a piece of information to be used, it must be referenced at least once in your idea.

I know for loops, ds grids and arrays are probably the answer to my problem, but I just don't grasp the ideas behind them
This is the root cause of your problem, and the part you need to work on solving first. Then you can return to the remaining part of the problem, which will be the simple matter of applying the knowledge that you most definitely need if you're going to make the type of game you're talking about.
 
I have no idea what the script in the screenshot is supposed to do, so I can't comment on that.

What I would do instead is use a friendly ds_list structure, containing unique identifiers of specific tiles.
They can correspond to tile instances ids, or maybe to indices in a grid (0-9 for first row, 10-19 for second row and so on); it depends on how you implemented the grid in the first place. The important part is that:
- they are unique
- you can easily obtain the tile id given the tile
- you can easily obtain the tile letter given tile id

The general idea is that:
1. When you select a tile, you add that tile identifier to the list:
GML:
ds_list_add(selected_tiles, _tile_id);[code]

2. When you deselect a tile, you remove that identifier from the list:
[code=gml]ds_list_delete(selected_tiles, ds_list_find_index(selected_tiles, _tile_id));
3. After selection changes, you rebuild the string from the selected tiles, in the order they've been last selected:
GML:
selected_text = "";
for (var i = 0; i < ds_list_size(selected_tiles); i++) {
  var _tile_id = selected_tiles[| i];
  selected_text += _tile_id.letter;
}
Generally, I wrote that code with assumption that the identifiers are tile instances ids (rather than e.g. grid indices). Hopefully it won't be a problem for you to adjust the code to your needs.
wow, you wrote all of that very quickly. I wish I understood for loops like you do. I will try to get your code to work with my code. Thank you so much. I appreciate it.

So... you're looking at the width, and thus effectively the length, of the string. The entire string. And using that to delete a letter. At what point in this idea does the position of the chosen character in the string come into it? Surely, you must at least understand that in order for a piece of information to be used, it must be referenced at least once in your idea.



This is the root cause of your problem, and the part you need to work on solving first. Then you can return to the remaining part of the problem, which will be the simple matter of applying the knowledge that you most definitely need if you're going to make the type of game you're talking about.
It's in the code. For example:
if string_width(global.message)>=0 && string_width(global.message)<=32{
global.pos=string_delete(global.message,1,1);
}
if the string width is greater than or equal to 0 and it's less than or equal to 32 pixels, then that's letter position 1, because the font size is 32.
The reason I thought this would work was because it's measuring every 32 pixels of the string, and assigning that position to the correct string_delete code, which gets passed into my code else where. But like I said, I'm not new to GML, but I'm not exactly a pro at it. But thanks for your... pep talk?
 

Alice

Toolmaker of Bucuresti
Forum Staff
Moderator
If you want to count the number of characters in string, I'd suggest string_length rather than string_width.
string_width is better suited for e.g. deciding where to draw a string based on the width of text to be drawn, and it takes into account varying letter widths, kerning etc. And font size being 32 doesn't mean specific characters will be 32px tall or wide; fonts use points scale which is slightly different from pixels.

So if you e.g. want to delete the last character of the string, you can do:
GML:
target_string = string_delete(source_string, string_length(source_string), 1);
(of course, it won't be needed if you opt for my solution, because in that case the string is rebuilt every time selected tiles are changed)
 

Nidoking

Member
if the string width is greater than or equal to 0 and it's less than or equal to 32 pixels, then that's letter position 1, because the font size is 32.
Sure, yes, fine. Let's suppose you have seven characters in your string. So it's 7 * 32 pixels long. Which means that you want to delete the seventh character, according to your thinking. Correct me if I'm wrong, but I thought the problem was "I want to delete some specific character in the string that isn't necessarily the last one." Your statement doesn't even have room for a variable to tell me which character to delete. You're literally ignoring the most important part of your own problem here. If you always want to delete the last character, there are simpler ways to do it, and also it's not what you stated was your problem in the first post. I'll say it again: If you want to use a certain piece of information to solve a problem, you need to have that piece of information. That information must somehow be used in your solution, somewhere. You must realize that this vital piece is missing from your statement.
 
Sure, yes, fine. Let's suppose you have seven characters in your string. So it's 7 * 32 pixels long. Which means that you want to delete the seventh character, according to your thinking. Correct me if I'm wrong, but I thought the problem was "I want to delete some specific character in the string that isn't necessarily the last one." Your statement doesn't even have room for a variable to tell me which character to delete. You're literally ignoring the most important part of your own problem here. If you always want to delete the last character, there are simpler ways to do it, and also it's not what you stated was your problem in the first post. I'll say it again: If you want to use a certain piece of information to solve a problem, you need to have that piece of information. That information must somehow be used in your solution, somewhere. You must realize that this vital piece is missing from your statement.
it deletes it in the code. string_delete(global.message, 1, 1); <- The first "1" inside the parenthesis is the position to delete. So in the code it determines the width of the string, it chops the width up into 32 pixel chunks, and whatever exponent of 32 is found, it adds in the string_delete() function to my code else where, through a variable at the top called "pos". So let me show you again what my thinking was:

global.pos=string_delete(global.message,0,0); //this would be the initial variable.

if string_width(global.message)>=0 && string_width(global.message)<=32{ //if the character between 0 and 32 is there...
global.pos=string_delete(global.message,1,1); //pass this code into the other code else where in the game.
}

So I do have a way of determining the position of the letter, a variable to plug into other code, and the code it needs to plugin, with the correct position in the code to delete. The "visual" part you're looking for is in another part of the game that handles that. This was just a script to take care of the position of the letter, and to create the code the rest of the code elsewhere, needed to work. I also said it didn't work, I also said that I could delete the whole string easier with 1 line of code rather than a whole script, and I also said repeatedly I'm not good at GML, and finally I already know I need to learn the things I don't know. So I'm not sure how I'm ignoring anything. I am just trying to figure out why the code I wrote up there wouldn't work. So far you told me that I didn't know the position (I then said I did and showed you), and now you're telling me that I don't have a way to erase it, when it's the "guts" of the if statement. With all that being said, thank you for your help. I will learn for loops, arrays, and ds_everythings. Cool?
 
Is global.pos supposed to be a numeric variable?
No, and that's actually probably where it goes bad. What happens is when the variable is initialized it starts off with string_delete(global.message,0,0); this way it has some starting value. Then when the conditions are met, it changes to a specific version of the "sting delete" code, according to the size of the string_width. So say it's the second character, it would be between 32 and 64 pixels, and if that's true, then it would get this code: string_delete(global.message,2,1);
 

chamaeleon

Member
No, and that's actually probably where it goes bad. What happens is when the variable is initialized it starts off with string_delete(global.message,0,0); this way it has some starting value. Then when the conditions are met, it changes to a specific version of the "sting delete" code, according to the size of the string_width. So say it's the second character, it would be between 32 and 64 pixels, and if that's true, then it would get this code: string_delete(global.message,2,1);
Just asked because "pos" sounds like position which typically a number, combined with the fact that string_delete does not modify the string passed to it but rather returns the string value with the segment removed. So if pos is supposed to be the resulting string with removed characters, that's fine. It was only a thought.
 
Just asked because "pos" sounds like position which typically a number, combined with the fact that string_delete does not modify the string passed to it but rather returns the string value with the segment removed. So if pos is supposed to be the resulting string with removed characters, that's fine. It was only a thought.
yeah it was originally going to be a number, but the way that I tried didn't work (obviously) but I'm bad at coming up with names for my variables, so I just modified the one I already had to mean something else. I was actually about to name it "potato" because I couldn't think of what to call it. lol
 

Fredrik

Member
Im sorry if im missunderstanding, as I'm lazy and didnt read the entire thread and commends, but if you want to delete a specific letter in a string cant you use string_replace(str, substr, newstr); ? With the first entry being the string, second the letter you want to delete, and the last to replace it with nothing, by using " ".
 

Nidoking

Member
it adds in the string_delete() function to my code else where, through a variable at the top called "pos"
Ah, see, this is your problem. You can't store a closure in a variable. And if you don't know what a closure is, then I consider my point made. Short answer, you can't store the idea "call this function with these arguments" in a variable. You could maybe store it as a string, parse the string using reflection, and execute that, but no. For the love of all that is sane in this world, no, especially when you still haven't grasped the point I'm trying to make, which is also your problem. Please answer this simple question: Which of the following criteria determines which character should be removed from the string?
A. Which tile was chosen, and the corresponding position in the string of the letter that came from that tile
B. How many tiles have been chosen

Your method will always remove the last character from the string, regardless of which tile is clicked. That is exactly what you said, in your opening post, that you don't want to happen. If your answer was B, then congratulations, this post was a complete waste of everyone's time. So I'm going to assume that you will answer A. Now, take another look at your if construction. Assuming it were to work the way you describe, which it doesn't, tell me where in that block is the information about which tile was clicked? What is the variable that tells you which character in the string corresponds to that letter... and why don't you just use that as the argument to string_delete?
 
Ah, see, this is your problem. You can't store a closure in a variable. And if you don't know what a closure is, then I consider my point made. Short answer, you can't store the idea "call this function with these arguments" in a variable. You could maybe store it as a string, parse the string using reflection, and execute that, but no. For the love of all that is sane in this world, no, especially when you still haven't grasped the point I'm trying to make, which is also your problem. Please answer this simple question: Which of the following criteria determines which character should be removed from the string?
A. Which tile was chosen, and the corresponding position in the string of the letter that came from that tile
B. How many tiles have been chosen

Your method will always remove the last character from the string, regardless of which tile is clicked. That is exactly what you said, in your opening post, that you don't want to happen. If your answer was B, then congratulations, this post was a complete waste of everyone's time. So I'm going to assume that you will answer A. Now, take another look at your if construction. Assuming it were to work the way you describe, which it doesn't, tell me where in that block is the information about which tile was clicked? What is the variable that tells you which character in the string corresponds to that letter... and why don't you just use that as the argument to string_delete?
Wow. Look man, I got my answer, thanks to Alice helping me out, and I've been trying to be nice to you since your first post. Thank you for your time, and your effort. I no longer need assistance in the matter. I understand that you know more about this than I do, but the more you frankly act like a dick, the more I want to ignore you. If you can't talk to me like some one who needs help, rather than someone you need to admonish, I have no further need to talk to you. Let's just say you were right about me all along, and you stand victorious in all your condescending, brow-beating glory. Now, please move along, as I don't need your brand of helping. Good bye.
 

Nidoking

Member
You definitely need help. I just don't know whether you even have enough skill yet to understand what help it is that you need. Your ideas about how programs work are very far from reality, and you don't appear to want to understand that. This problem is going to get in your way many more times if you continue to try to make games. Well, okay. As long as you're happy with "go to the forum and ask about very basic things" being part of your default workflow instead of understanding what you're doing, I guess it gets you where you need to go, whether you know where that is or not.
 
Top