PDA

View Full Version : Scoreboard


xAndrewx
11-15-2014, 07:30 AM
So, I started working on a scoreboard script.

Here's what I have so far-

const MAX_STATS = 26;

function onCreated() {
this.registerStat("Rocks_Staff3", "xAndrewx", 4);
}

public function registerStat(temp.stat, temp.pl, temp.value) {
if (temp.stat != "Rocks_Staff3") return;

//Create place holders if no stats exist
if (this.("Stat_" @ temp.stat @ "_" @ 1) == null) {
for (temp.i = 1; temp.i <= MAX_STATS; temp.i++) {
this.("Stat_" @ temp.stat @ "_" @ temp.i) = {27 - temp.i, "(npc-server)", "Place Holder", "head19.png"};
this.("Stat_PlayerCache_" @ temp.stat).add("(npc-server" @ temp.i @")");
}
}

//Loop from lowest score to highest
for (temp.i=MAX_STATS; temp.i>=1; temp.i--) {
temp.data = this.("Stat_" @ temp.stat @ "_" @ temp.i);

temp.score = temp.data[0];
temp.owner = temp.data[1];

//Not hit the lowest score- end the loop
if (temp.i == MAX_STATS) {
if (temp.value <= temp.score) {
break;
}
}

if (temp.value > temp.score) {
//If it's the last entry, finally add it in!
if (temp.i == MAX_STATS) continue;

//Replace old stats with new score
this.("Stat_" @ temp.stat @ "_" @ (temp.i + 1)) = temp.data;
this.("Stat_PlayerCache_" @ temp.stat)[(temp.i + 1)] = temp.owner;

//If they've beaten the high score
if (temp.i == 1) {
this.("Stat_" @ temp.stat @ "_" @ temp.i) = {temp.value, temp.pl, findplayer(temp.pl).nick, findplayer(temp.pl).head};
this.("Stat_PlayerCache_" @ temp.stat)[(temp.i)] = temp.pl;
// echo("-Beat High Score- Adding stat in at" SPC temp.i SPC "old-" @ temp.score @ ". new-" @ temp.value @ ".");
}
} else {
//Replace the score where they should sit
this.("Stat_" @ temp.stat @ "_" @ (temp.i + 1)) = {temp.value, temp.pl, findplayer(temp.pl).nick, findplayer(temp.pl).head};
this.("Stat_PlayerCache_" @ temp.stat)[(temp.i + 1)] = temp.pl;
break;
}
}

this.trigger("update", "");
}


So, I'm wanting a system which only displays the player in the list once.

The only way to do this is another loop- but what do you guys think?? Can you think of an alternative & more robust method

BlueMelon
11-15-2014, 01:47 PM
Basically what you want to do is create a sorted array?

Look into the sortByValue function.

TGraalVar.sortbyvalue(str, str, bool) - sorts an array, specify the variable of the array members which is compared, also the variable type and if it should be sorted ascending; variable type can be "string", otherwise it is sorted by floating point value

Example:

function onCreated() {

temp.plScores = {
{"BlueMelon",9001},
{"snk",69},
{"BigMonster",1337},
{"supaman771",7331}
};

temp.board = null;
for(temp.plScore : temp.plScores) {
temp.board.add(temp.plScore[0]); // acc
temp.board[temp.board.size() - 1].score = temp.plScore[1]; // score
}

temp.board.sortByValue("score", "float", false);

echo("Scoreboard: ");
for(temp.i=0; temp.i<temp.board.size(); temp.i++) {
temp.acc = temp.board[temp.i];
temp.score = temp.board[temp.i].score;
echo(temp.i+1 @". "@temp.acc SPC "with" SPC temp.score);
}
}


which gives

Scoreboard:
1. BlueMelon with 9001
2. supaman771 with 7331
3. BigMonster with 1337
4. snk with 69

xAndrewx
11-15-2014, 01:49 PM
no

With that system you're storing millions of variables - I only want it to store the top 25 scores

p.s. love the 69 ;)

BlueMelon
11-15-2014, 02:15 PM
That was just an example, change it to fit your needs :p

What you want to do seems simple enough,

temp.board= {25 values};
temp.board.sortdescending();

// add a new entry
temp.newEntry = 123;
temp.lowest = temp.board[temp.board.size()-1];
if(temp.newEntry > temp.lowest) {
// delete lowest
// add to board
// re-sort
}
// display or whatever


I can whip up another example later tonight if needed.

xAndrewx
11-15-2014, 08:31 PM
sure

don't store more than 25 scores in the DB though

scriptless
11-16-2014, 03:23 AM
I don't like DBNPC's being used like that anymore. I have had way to many problems with the DBNPC flags being reset randomly =/

BlueMelon
11-16-2014, 03:52 AM
sure

don't store more than 25 scores in the DB though

>_<

Try this.

/* Scoreboard system */

function onCreated() {
this.numEntrys = 25;
clearBoard();

addScore("BlueMelon",9001);
addScore("snk",69);
addScore("BigMonster",1337);
addScore("supaman771",7331);

displayBoard();
}

function displayBoard() {
for(temp.i=0; temp.i<this.board.size(); temp.i++) {
echo(temp.i+1 SPC this.board[temp.i] SPC this.board[temp.i].score);
}
}

function clearBoard() {
this.board = {};
for(temp.i=0; temp.i<this.numEntrys; temp.i++){
this.addScore("(npc server)", 0);
}
}

public function addScore(temp.acc, temp.score) {
this.board.sortByValue("score", "float", false);

temp.lowest = this.board[this.board.size() -1];

if(this.board.size() < this.numEntrys){
this.board.add(temp.acc);
this.board[this.board.size() -1].score = temp.score;
}else if(temp.score > temp.lowest.score && temp.score > 0) {
this.board.delete(this.board.size() -1);
this.board.add(temp.acc);
this.board[this.board.size() -1].score = temp.score;
}

this.board.sortByValue("score", "float", false);
}

xAndrewx
11-16-2014, 07:12 AM
Brilliant, thank you very much! (I repped you- you now have 3 bars! **** yeah)

Here's my final piece

const MAX_STATS = 25;

function onCreated() {
temp.statsize = 5;
temp.stats = getStat("PlantsPicked");
for (temp.i = 0; temp.i < 5; temp.i++) {
global.out(temp.stats[temp.i], "Debug");
}
}

//Return Account, Score, {Nickname, Head, Hat}
public function getStat(stat) {
for (temp.i = 0; temp.i < MAX_STATS; temp.i ++) {
temp.stats.add({this.("board_" @ temp.stat)[temp.i], this.("board_" @ temp.stat)[temp.i].score, this.("board_" @ temp.stat)[temp.i].outfit});
}

return temp.stats;
}

public function registerStat(stat, pl, score) {
//Create placeholders
if (this.("board_" @ temp.stat) == null) {
echo("Clearing Board-" SPC temp.stat);
this.onClearBoard(temp.stat);
}
//Sort in descending order
this.("board_" @ temp.stat).sortByValue("score", "float", false);

//If they already have a score, just update it. No duplicates!
temp.found = this.("board_" @ temp.stat).index(temp.pl);
if (temp.found >= 0) {
this.("board_" @ temp.stat)[temp.found].score = temp.score;

//Update outfit
temp.pla = findplayer(temp.pl);
if (temp.pla != null)
this.("board_" @ temp.stat)[this.("board_" @ temp.stat).size() -1].outfit = {temp.pla.nick, temp.pla.head, temp.pla.attr[1]};
} else {
//Board size
temp.board_size = this.("board_" @ temp.stat).size();

//Get lowest score
temp.lowest = this.("board_" @ temp.stat)[temp.board_size -1];

if(temp.board_size < MAX_STATS){
this.("board_" @ temp.stat).add(temp.pl);
this.("board_" @ temp.stat)[temp.board_size - 1].score = temp.score;
}else if(temp.score > temp.lowest.score && temp.score > 0) {
//Delete lowest score
this.("board_" @ temp.stat).delete(temp.board_size - 1);

//Store score
this.("board_" @ temp.stat).add(temp.pl);
this.("board_" @ temp.stat)[this.("board_" @ temp.stat).size() -1].score = temp.score;

//Store outfit
temp.pla = findplayer(temp.pl);
if (temp.pla != null)
this.("board_" @ temp.stat)[this.("board_" @ temp.stat).size() -1].outfit = {temp.pla.nick, temp.pla.head, temp.pla.attr[1]};

}
}
this.("board_" @ temp.stat).sortByValue("score", "float", false);

this.trigger("update", "");
}

//Clear stats (only called when creating a new stat)
function onClearBoard(stat) {
this.("board_" @ temp.stat) = {};
for(temp.i = 0; temp.i < MAX_STATS; temp.i++) {
this.registerStat(temp.stat, "(npc server)", 1);
}
}

xAndrewx
11-16-2014, 07:15 AM
I don't like DBNPC's being used like that anymore. I have had way to many problems with the DBNPC flags being reset randomly =/

p.s. add this once you add / remove any variables


this.trigger("update");


It saves the NPC :)

Inari
11-16-2014, 10:54 AM
Couldn't you just like, use SQL, have a database with each player + their scores, update the player entry and build the scoreboard using something like
SELECT * FROM myTable ORDER BY Stat_Blah ASC LIMIT 25
?

xAndrewx
11-16-2014, 04:43 PM
no, i don't want to store EVERY players score to only pull back the highest 25.

Inari
11-16-2014, 06:06 PM
Yeah, but why? :3 seems nice to have the saves anyway and its a few bytes per player

scriptless
11-17-2014, 01:42 AM
p.s. add this once you add / remove any variables


this.trigger("update");


It saves the NPC :)

Not always. We were doing this on the scoreboard on GK and it still had issues.

xAndrewx
11-18-2014, 04:11 AM
Why would you store unnecessary information that will never be used if you don't have to.

Inari
11-18-2014, 01:00 PM
Well it has to be stored somewhere either way, SQL is nice for backups like that imo. Also you get a more central place to manage such info than the client vars provide.

Cubical
11-18-2014, 01:32 PM
why don't you just keep track of high scores on a notepad. if internet goes down u have hard copy

Inari
11-18-2014, 02:22 PM
why don't you just keep track of high scores on a notepad. if internet goes down u have hard copy

Possible, but that isn't very dynamic, and doesn't make much sense.
It makes sense to store player-data in a central place where its easily accessible via a language made for accessing such entries

fowlplay4
11-18-2014, 02:33 PM
why don't you just keep track of high scores on a notepad. if internet goes down u have hard copy

i also backup all my data to offsite stone tablets.

Tim_Rocks
11-18-2014, 02:33 PM
why don't you just keep track of high scores on a notepad. if internet goes down u have hard copy

Cubical brings up an excellent point here.

Inari
11-18-2014, 03:17 PM
Pros of SQL (some only apply if using a primarily SQL-based system, so probably not to all graal servers :p):

Easy access to all data
Made to store and request data (and has proven itself)
Doesn't break from staff editing the attributes
Better relational data management

among others

(Of course storing all data in NPC-flags is also a way, but then you end up having to write more potentially slower and buggier script code)

Tim_Rocks
11-18-2014, 03:26 PM
Yea, but there's no need to create an SQL table for over a thousand players when all you really need is 25 of them.

Inari
11-18-2014, 03:38 PM
Yea, but there's no need to create an SQL table for over a thousand players when all you really need is 25 of them.

Well, no direct /need/, except if you use SQL to begin with :D Which maybe graal servers should consider doing... I guess its more of a point of "hey, if you put your structure more around SQL you can make such things much much easier"~

Cubical
11-18-2014, 03:53 PM
Well, no direct /need/, except if you use SQL to begin with :D Which maybe graal servers should consider doing... I guess its more of a point of "hey, if you put your structure more around SQL you can make such things much much easier"~

Well, no direct /need/, except if you use SQL to begin with Which maybe graal servers should consider doing... I guess its more of a point of "hey, if you put your structure more around SQL you can make such things much much easier"~

Inari
11-18-2014, 03:57 PM
Well, no direct /need/, except if you use SQL to begin with Which maybe graal servers should consider doing... I guess its more of a point of "hey, if you put your structure more around SQL you can make such things much much easier"~

Well, no direct /need/, except if you use SQL to begin with Which maybe graal servers should consider doing... I guess its more of a point of "hey, if you put your structure more around SQL you can make such things much much easier"~
(am i missing something?)

Anyway, my original point still is valid I guess: If you use SQL you have a much cleaner code and the < 1 MB you use to store data doesn't bother anyone ever.

Cubical
11-18-2014, 04:06 PM
Well, no direct /need/, except if you use SQL to begin with Which maybe graal servers should consider doing... I guess its more of a point of "hey, if you put your structure more around SQL you can make such things much much easier"~
(am i missing something?)

Anyway, my original point still is valid I guess: If you use SQL you have a much cleaner code and the < 1 MB you use to store data doesn't bother anyone ever.

Well, no direct /need/, except if you use SQL to begin with Which maybe graal servers should consider doing... I guess its more of a point of "hey, if you put your structure more around SQL you can make such things much much easier"~
(am i missing something?)

Anyway, my original point still is valid I guess: If you use SQL you have a much cleaner code and the < 1 MB you use to store data doesn't bother anyone ever.

Tim_Rocks
11-18-2014, 04:55 PM
I use SQL for most systems on Era. Works nicely.

Carlito
12-03-2014, 09:05 PM
SQL is great i use it mostly but at the same time it can easily be abused and bloat your db. If Snk only wanted the top 20 players why would he store and update over 3000 accounts. I feel for this reason his method is best, it is only maintaining the top 25 oppose of all 3000+ players. DB npcs are great for temp storage especially on situations like this.

prozacboy666
12-11-2014, 11:02 PM
Use SQL. Here is how I would do it.
When a new score comes in do a select on the database for the minimum score to get into the top20.

SELECT MIN(score) FROM myTable;

If new score > min, insert new score into the database and delete the min score.
The only thing is you would have to do two queries to delete the min score from the database something like

minId = SELECT id FROM myTable WHERE score = (SELECT min(score) FROM myTable)
DELETE FROM top20 WHERE id = minId

Then all you have to do is selects on that table if you are looking for the top20

SELECT * FROM top20 ORDER BY score DESC


You probably could get the ID and delete it from the database all in one query but this should be fast enough.

Elk
12-12-2014, 01:12 AM
i also backup all my data to offsite stone tablets.

cuneiform <3

xAndrewx
12-12-2014, 08:48 AM
Use SQL. Here is how I would do it.
When a new score comes in do a select on the database for the minimum score to get into the top20.

SELECT MIN(score) FROM myTable;

If new score > min, insert new score into the database and delete the min score.
The only thing is you would have to do two queries to delete the min score from the database something like

minId = SELECT id FROM myTable WHERE score = (SELECT min(score) FROM myTable)
DELETE FROM top20 WHERE id = minId

Then all you have to do is selects on that table if you are looking for the top20

SELECT * FROM top20 ORDER BY score DESC


You probably could get the ID and delete it from the database all in one query but this should be fast enough.

You'd need three queries.
1,) Find the minimum score
2,) Add the score
3,) Delete old score


I guess it's what you want to store.

prozacboy666
12-12-2014, 05:13 PM
You'd need three queries.
1,) Find the minimum score
2,) Add the score
3,) Delete old score


I guess it's what you want to store.

I just find it easier because when you want to display the top20 scores, in SQL is does all the work for you and faster than getting all the results and then do some type of sorting function.

Did you use another solution? I am curious on how you did it.

cbk1994
12-14-2014, 05:49 AM
Using SQL when you have a small fixed number of rows that you're not even performing real queries on doesn't make any sense.

If all you want is a list of the top 25 players, store them in an array in a DB NPC flag. There are built-in (fast) functions for sorting arrays, and performance is not an issue here anyway (but I'd be willing to bet this is faster than constantly shuffling things in/out of SQLite, anyway).

The only way I see the code being simpler is if you store all players in a table, and use a query (SELECT .. ORDER BY .. LIMIT 25), which is how I would recommend doing it.