Loading a string of data seperated by spaces and comma's

Hi.

I’m trying to load in data from the Runescape hiscores, the data structure is as follows:

Rank,Level,Xp(space)Rank,Level,Xp(space) etc.

Example data: https://secure.runescape.com/m=hiscore_oldschool/index_lite.ws?player=swampletics

559866,1305,16020717 448750,76,1336860 507980,73,993001 326590,90,5584463 471125,84,2983784 834444,71,830231 823142,49,94297 896577,65,466827 1103301,50,111220 629804,65,481761 733809,55,166760 655933,62,365270 421971,66,528570 673282,56,194564 736185,50,107826 448109,64,423037 654094,42,48234 456095,63,402594 257834,63,380955 572231,63,369397 704338,25,8146 -1,1,0 740105,19,4115 360108,53,138805 -1,-1 -1,-1 -1,-1 -1,-1 -1,-1 -1,-1 -1,-1 -1,-1 -1,-1

How can I load these into prolog so I can use them for equations, e.g see if entry 3 (players defence level) is higher than x?

I want to use assert to save the levels so they can be used when asking questions (for chatbot).

Thanks,
Jan Julius

You could split the input on “white space”, then each element on comma. I will use http/http_open and strings for this.

(it seems the content is separated by newlines but this doesn’t matter too much. Just split on spaces, tabs, newlines.

$ cat runescape.pl 
:- use_module(library(http/http_open)).

load_highscores(URL, Highscores) :-
    setup_call_cleanup(
        http_open(URL, In, []),
        process(In, Highscores),
        close(In)).

process(In, Highscores) :-
    read_string(In, _, Str),
    split_string(Str, "\n\t ", "\n\t ", Lines),
    maplist(line_to_scores, Lines, Highscores).

line_to_scores(Line, Scores) :-
    split_string(Line, ",", "", Str_scores),
    maplist(number_string, Scores, Str_scores).

If you run this code you’ll see you now have a list of lists:

?- load_highscores('https://secure.runescape.com/m=hiscore_oldschool/index_lite.ws?player=swampletics', Result).
Result = [[559896, 1305, 16020717], [448777, 76, 1336860], [507995, 73, 993001], [326604, 90, 5584463], [471151, 84, 2983784], [834489, 71, 830231], [823173, 49|...], [896481|...], [...|...]|...].

You can now go over the lines and assert them one by one, or just save the whole thing and get the lines by their number. It really depends though how you are going to use them.

Hi Boris,

I will check this out tomorrow and come back to you with the result thank you a lot for your time.

Hi Boris,

Your answer has helped me loads thanks for the contribution.

I have another question I will post it here, I could make a new topic too if you want but here it is (please do note I just do programming in C language usually):

Now with the data saved in list I can use it as so with this predicate:
myskill(Skill, Level).

So for instance I can do

?- myskill(“Attack”, X).
X = 50;

Now, There are quests taht require levels I defined them as such in my prolog:

requires_skill(heroesquest, ‘53’, “Cooking”).
requires_skill(heroesquest, ‘53’, “Fishing”).
requires_skill(heroesquest, ‘25’ “Herblore”).
requires_skill(heroesquest, ‘50’, “Mining”).

Now I want to check if the player has the levels for a certain quest

So I came up with the following:

can_do_quest(Quest, Result) :-
requires_skill(Quest, Rl, Skill),
atom_number(Rl, ReqLevel),
myskill(Skill, Level),
Result is Level > ReqLevel.

But it does not seem to work

thank you again.

What does that mean? Do you get compilation errors? Warnings? Or it does something different from what you expect? What do you expect? How can I reproduce your problem?

Oh yeah, the error: ERROR: Undefined procedure: can_do_quest/2 (DWIM could not correct goal)

Also the goal is if the mystats returns a value lower than the skill it is checking it should return false and if no skill is lower than the required skill it should return true.

If you can share some code that I can copy-paste and you tell me what you did with it, then I can do as you did and see the problem. It becomes very easy to tell you what is wrong with it. If I have to reconstruct what you might have written and what you might have done and then tell you my opinion of how I feel about it, it becomes very difficult.

But the error you show means that something definitely went wrong, and can_do_quest/2 is not defined at all. I see other problems as well, for example Result is Level > ReqLevel is almost certainly an error?

PS: use markdown for code. See this post.

Ah yeah I will in the future but to not clutter the post I have the code in a pastebin here:

https://pastebin.com/YU0DqD3h

Thank you for your time!

test data:

-? load_skills('lynx titan', Result), can_do_quest(heroesquest).

should return true

-? load_skills('swampletics', Result), can_do_quest(heroesquest).

should return false

Well, if I put your code in a file and try to compile it, I get two errors and a warning. I don’t think you can expect much before you figure out what the errors are.

How do you compile the code? I just load up the file using the command prompt.

You show me yours I’ll show you mine. How do you load your code using the command line? You can just copy-paste whatever it is you type in and the output, it can’t be that much?

Oh, I figured it out no problem I didnt realise using the executable compiles the code sorry that is my bad.

But I’ve fixed the errors:

https://pastebin.com/8mBP9mXD

As for what I’m trying to achieve.

I want to check all the levels for a quest in this case the heroesquest if any of the players levels is below the required ones it should return false if this is not the case it should return true I just have trouble figuring out how to do it.

My attempt at this:

can_do_quest(Quest, Result) :-
	requires_skill(Quest, Rl, Skill), 
	atom_number(Rl, ReqLevel),
	myskill(Skill, Level), 
	Level @>= ReqLevel.

requires_skill(heroesquest, '53', "Cooking").
requires_skill(heroesquest, '53', "Fishing").
requires_skill(heroesquest, '25', "Herblore").
requires_skill(heroesquest, '50', "Mining").

This works however it returns all the instances of the level comparisons e.g results:

A player missing one level expected false
actual:

can_do_quest(heroesquest,X).
true ;
true ;
true.

a player missing no levels expected: true
actual:

can_do_quest(heroesquest,X).
true ;
true ;
true ;
true.

thanks again.

Also I would like to add I’m probably thinking too much of methods than predicates.

I do the following: I take the complete source as you have put on pastebin, then save it to a file called rs.pl. Then, in the same directory as the file:

$ swipl -q
?- [rs].
Warning: .../rs.pl:42:
	Singleton variables: [X]
Warning: .../rs.pl:55:
	Singleton variables: [Result]
true.

Don’t ignore warnings. They are almost always errors in SWI-Prolog.

Either way, if you want to make sure that all the requirements are fulfilled, you might need something like foreach/2. I am not sure, but something like:

foreach(
    requires_skill(heroes_quest, Level, Skill),
    ( myskill(Skill, My_level), My_level >= Level) )

In a more procedural reading, what you have right now is about the same as:

for skill in required_skills:
   if have skill then print "true"

and what you probably want is:

if have skill A
        AND have skill B
        AND ...
        AND have skill Z
   then print "true"

You can use foreach/2 to make this chain of ANDs for you. The “conjunction” the documentation talks about is exactly this a AND b AND ...

You are a hero, I got it.

This was what got me what I wanted:

can_do_quest(Quest) :-
	foreach(
     (requires_skill(Quest, Rq, Skill), atom_number(Rq, Level),myskill(Skill, My_level)),
		(My_level @>= Level) ).

Thank you so much for your help and time Boris I have managed to understand Prolog a lot more with your assitance.

Great. Two small remarks. First, @>= is for standard order comparison. Considering you converted to numbers, you probably want >=. Second, foreach/2 requires some understanding. In most cases where you merely have a generator and independent checks you can use the much simpler forall/2. This is simple defined as \+ (Generator, \+Test), i.e., there is no answer from Generator for which Test fails.

Aah, I see. I admit I got trigger-happy because I have been trying to find a good excuse to use foreach/2 for a long time. Now after your comments I realize that a) it doesn’t seem to be needed in this case and b) I still haven’t found a realistic use case for foreach/2 and finally c) I don’t have the understanding that foreach/2 requires.

Now that I read the documentation yet again, I see:

The implementation executes forall/2 if Goal does not contain any variables that are not shared with Generator.

And the code clearly says:

  562 foreach(Generator, Goal) :-
  563    term_variables(Generator, GenVars0), sort(GenVars0, GenVars),
  564    term_variables(Goal, GoalVars0), sort(GoalVars0, GoalVars),
  565    ord_subtract(GoalVars, GenVars, SharedGoalVars),
  566    (   SharedGoalVars == []
  567    ->  \+ (Generator, \+Goal)      % = forall(Generator, Goal)
  568    ; % and so on

So the can_do_quest/1 definition above ends up in line 567, just as a query like:

?- foreach(between(1, 3, X), between(1, 5, X)).

Foreach creates a conjunction from the answers of the generator. That is, it does not call essentially \+ \+ Test but simply Test. This makes a difference if you want to maintain the bindings or choice points of Test. The first is notably useful for constraints. For example

foreach(some(P), dif(X, P))

This works fine, while if you use this with forall/2 it won’t. I think you can figure out why.

Thanks to both of you for your input, you guys have been of immense help.