JavaScript for moddding

MrUnderhill89
Member
Posts: 18
Joined: Fri Jun 03, 2016 13:55
GitHub: mrunderhill89

Re: JavaScript for moddding

by MrUnderhill89 » Post

The only place I can see where Javascript would be useful in Minetest is if we switched out Formspecs for some kind of HTML-based UI, which would be a nice replacement, but even then I'd prefer using Lua. Not every programming language is the right tool for every application. If your first impulse when encountering a new language is to demand a complete re-write in a language you already know instead of working with what's already in use, then that doesn't bode well for your career as a developer. A little flexibility goes a long way.

drkwv
Member
Posts: 102
Joined: Thu Jun 28, 2012 13:48
GitHub: aa6

Re: JavaScript for moddding

by drkwv » Post

As for now both LUA and JS suck. They added tons of new syntax in JS which sucks. For example classes: WTF??? I'd say to consider coffeescript but it sucks A LOT too (although has a good parts which gone wrong).

And PLEASE GOD no Python. Never. Python is a language which doesn't even have "true" and "false" constants. And it's drowning in a swamp on it's own versions, you will never guess what version should you choose to run the script (and the script won't run anyway because of some cryptic obstacles).

Still pro-JavaScript though because learning the Lua and it's schizophrenic quirks to make a small minetest mod is an overkill. Working with arrays in Lua is always google://why_should_i_always_google_how_to_cycle_through_array_in_lua_why_wont_you_make_it_as_simple_as_in_another_languages_what_is_wrong_with_you.
JS is more common and has a C-like syntax. It's even more common now because it has it's "classes" thingy.

BTW, Happy New Year everyone.
Last edited by drkwv on Sat Jan 02, 2021 11:21, edited 1 time in total.

drkwv
Member
Posts: 102
Joined: Thu Jun 28, 2012 13:48
GitHub: aa6

Re: JavaScript for moddding

by drkwv » Post

[duplicate post]

User avatar
Hume2
Member
Posts: 709
Joined: Tue Jun 19, 2018 08:24
GitHub: Hume2
In-game: Hume2
Location: Czech Republic

Re: JavaScript for moddding

by Hume2 » Post

To be honest, how many times did you see the "rewrite all of X to language Y" requests from community to succeed?
drkwv wrote:
Sat Jan 02, 2021 10:33
Python is a language which doesn't even have "true" and "false" constants.
Indeed. However, it has "True" and "False" constants ;)
If you lack the reality, go on a trip or find a job.

drkwv
Member
Posts: 102
Joined: Thu Jun 28, 2012 13:48
GitHub: aa6

Re: JavaScript for moddding

by drkwv » Post

Hume2 wrote:
Sat Jan 02, 2021 12:25
Indeed. However, it has "True" and "False" constants ;)
I wonder why not "truE" and "falsE". That's some fancy constants too! But not so fancy as ipairs. Ahh, those lovely ipairs. They could improve it also in a python-way by making it an "Ipairs" or "iPairs". General rule: the more stuff language has for programmer to remember - the better! DoesN't allow him to CoNcEnTrAtE on programming!

User avatar
v-rob
Developer
Posts: 970
Joined: Thu Mar 24, 2016 03:19
GitHub: v-rob
IRC: v-rob
Location: Right behind you.

Re: JavaScript for moddding

by v-rob » Post

drkwv wrote:
Sat Jan 02, 2021 13:00
Hume2 wrote:
Sat Jan 02, 2021 12:25
Indeed. However, it has "True" and "False" constants ;)
I wonder why not "truE" and "falsE". That's some fancy constants too! But not so fancy as ipairs. Ahh, those lovely ipairs. They could improve it also in a python-way by making it an "Ipairs" or "iPairs".
This is simply ridiculous. PascalCasing and camelCasing are deliberate coding styles. If JavaScript had ipairs, it would certainly be cased iPairs since JavaScript uses camelCasing for functions, so why are you complaining? You're arguing against yourself. And really, Lua having ipairs is a deliberate feature; those kinds of functions are iterators and have many uses. But if you really want to, you can write a JavaScript-like loop in Lua just fine:

JavaScript:

Code: Select all

var a = [1, 2, 3, 4, 5];
for (var i = 0; i < a.length; i++) {
    console.log(a[i]);
}
Lua:

Code: Select all

local t = {1, 2, 3, 4, 5}
for i = 1, #t do
    print(t[i])
end
Regardless, Minetest is using Lua, and there's essentially no way that's going to change.
Core Developer | My Best Mods: Bridger - Slats - Stained Glass

drkwv
Member
Posts: 102
Joined: Thu Jun 28, 2012 13:48
GitHub: aa6

Re: JavaScript for moddding

by drkwv » Post

v-rob wrote:
Sat Jan 02, 2021 20:25
PascalCasing and camelCasing are deliberate coding styles.
These are just excuses for someone's OCD. If I'd like to "obey the rules" I'd probably write directly in the bytecode. But I want a language, something that is made for humans. And humans tend to use these variants as well (and I've never seen a "True" anywhere before python):

Code: Select all

is_good = true
is_good = TRUE
v-rob wrote:
Sat Jan 02, 2021 20:25
If JavaScript had ipairs, it would certainly be cased iPairs
I'm glad it doesn't have it. Again, ipairs is something that you would meet only in Lua.
v-rob wrote:
Sat Jan 02, 2021 20:25
so why are you complaining?
Because I want to code and not to learn some exquisite quirks of language that are nor comfortable neither useful.
v-rob wrote:
Sat Jan 02, 2021 20:25
But if you really want to, you can write a JavaScript-like loop in Lua just fine:
No, that's not like it. I'm talking about iterating a hashtable. It goes like this:

JavaScript:

Code: Select all

// Putting ; after each line is a curse. I hate it. You forget it and then you get an error message. And then you fix it. And forget again. What a waste of efforts and time. I'm so glad that JavaScript has a great feature of not requiring it.
var arr = {
    "tom":"1",
    "jerry":"2",
    "wow-cool":"3"
}
for(var key in arr) { 
    console.log(arr[key]) // 1 2 3
}
Lua:

Code: Select all

arr = { 
    tom = "1", 
    jerry = "2" 
}
arr["wow-cool"] = "3"
print("123") -- this prints
for key,val in ipairs(arr) do
  print(key) -- this doesnt
end
-- Well, it didn't work. Again. I don't know what did I wrong, I just copied the code from my mod and it doesnt work here. I'm running it in a command line with lua5.3. Now I'm curious if this exact code will work in minetest... No it also does not. BUT WHY??? One moment, let me google it...
-- google://lua iterate table
for k, v in pairs(arr) do
  print(v) -- 3 1 2
end
-- Ahh, now it works. Could this be a bug in my code also? 
-- For some reason jerry comes first. WTF
-- google://lua pairs vs ipairs
a = {"one", "two", "three"}
for i, v in ipairs(a) do
  print(i, v)
end
-- Facepalm. It doesn't have ordered keys. Of course I should write my own implementation of hashtables to be able to do what other languages provide by default.
CoffeeScript:

Code: Select all

arr = 
    "tom":"1"
    "jerry":"2"
    "wow-cool":"3"
for key of arr
    console.log arr[key]
What I'd like to see:

Code: Select all

arr = 
    "tom":"1"
    "jerry":"2"
    "wow-cool":"3"
for key in arr
    console.log arr[key]

Code: Select all

arr = 
    "tom":"1"
    "jerry":"2"
    "wow-cool":"3"
for val of arr
    console.log val
v-rob wrote:
Sat Jan 02, 2021 20:25
Regardless, Minetest is using Lua, and there's essentially no way that's going to change.
That's understandable.

drkwv
Member
Posts: 102
Joined: Thu Jun 28, 2012 13:48
GitHub: aa6

Re: JavaScript for moddding

by drkwv » Post

Also Python has some perverted overcomplicated module system. You cannot simply dofile() there. At least I failed to do so and ended up with a 2k-lines-one-file-application (because I was need things to be done in the first place). Now my one an only python microservice will be rewritten on JavaScript, someday.

And also newest versions of Python and (sadly) Node.js do not support Windows 7. What a shame. Like if they had a share in Win10 sales forcing people to buy it. So you will have to stick with some old version of Python (and node) if you want to embed it. Because there is no real reason to drop OS support only because it's "old". Like if it suddenly became old overnight and everything broke in a moment.

User avatar
hex
Member
Posts: 82
Joined: Sun Dec 06, 2020 04:22
IRC: hecks
In-game: hex hhhehehe

Re: JavaScript for moddding

by hex » Post

Funny of you to call lua schizophrenic while championing a meme language like JS and being triggered over iterators. (not that ipairs makes any sense, you should always 'for i=1, #t do' because ipairs generates garbage)

Lua (5.1) is a very well designed language, a description of its entire grammar fits on one side of a business card, a runtime binary can fit in 64K and compiles on just about anything.

You can iterate over keys only by simply omitting the second loop variable, like

Code: Select all

for k in pairs( t ) do
	print( k );
end
which you'd know if you just bothered to RTFM.

Needing a sorted hashmap is another indicator of not knowing what you're doing. The speed of a hashmap or hash set comes from the fact that it isn't sorted. If you absolutely need to cripple a table with a sorting constraint, make a list of the keys and sort that. Wrap the whole thing in a container table, give it methods or hijack __index, even make your own iterator that spits out sorted pairs, and done.
That said, I don't think I've ever needed a contraption like this. If not bundling it with the language keeps the Python clowns out, I'm more than fine with it.

drkwv
Member
Posts: 102
Joined: Thu Jun 28, 2012 13:48
GitHub: aa6

Re: JavaScript for moddding

by drkwv » Post

hex wrote:
Sun Jan 03, 2021 11:59
Lua (5.1) is a very well designed language, a description of its entire grammar fits on one side of a business card, a runtime binary can fit in 64K and compiles on just about anything.
"Brainfuck" language has even smaller syntax and (I believe) even smaller binary! Why choose Lua over Brainfuck?
hex wrote:
Sun Jan 03, 2021 11:59
You can iterate over keys only by simply omitting the second loop variable, like

Code: Select all

for k in pairs( t ) do
  print( k );
end
which you'd know if you just bothered to RTFM.
The fun part is you completely ignore that I need an ordered hashtable. Not an array shuffle generator. Watch this:

Code: Select all

me@me:~/tst/lua$ node cycle2.js 
1
2
3
me@me:~/tst/lua$ node cycle2.js 
1
2
3
me@me:~/tst/lua$ node cycle2.js 
1
2
3
me@me:~/tst/lua$ lua5.3 cycle2.lua
2
1
3
me@me:~/tst/lua$ lua5.3 cycle2.lua
2
3
1
me@me:~/tst/lua$ lua5.3 cycle2.lua
3
1
2
me@me:~/tst/lua$ cat cycle2.js 
var arr = {"tom":"1","jerry":"2","wow-cool":"3"}
for(var key in arr) { 
    console.log(arr[key])
}
me@me:~/tst/lua$ cat cycle2.lua
arr = { tom = "1", jerry = "2" }
arr["wow-cool"] = "3"
for k in pairs( arr ) do
  print( arr[k] )
end
This is nonsense. Complete garbage. If I'd like to shuffle my hashtable I would use array.shuffle() or something. No one need this. No one expect this.
hex wrote:
Sun Jan 03, 2021 11:59
Needing a sorted hashmap is another indicator of not knowing what you're doing.
I don't think so. I just need an ordered hashmap/hashtable because it is extremely convenient. It's like not having a Math.round() in a Math library. BTW LUA DOESN'T HAVE IT LOL. Who needs a round function in 2021, right?
hex wrote:
Sun Jan 03, 2021 11:59
The speed of a hashmap or hash set comes from the fact that it isn't sorted.
I'm tired of these "speed" rants. You want speed -> write with Assembly language. You want more speed -> write bytecode. You want even more speed -> engineer an ASIC. You didn't grab a scripted language to gain speed. You need it to shorten the time you need to develop a program because your lifespan is short. That's the reason people invented scripted languages. So please doesn't mention speed. Lua is not about speed. It's 10^9x slower than ASIC microchip. Wouldn't it be great if minetest was an ASIC microchip and minetest mods were an ASIC microchips that you could attach to a minetest microchip??? Much speed would be gained then!

Again, you embed a scripted language into minetest while the minetest is written on C. That's a huge speed losses for nothing while you could write all mods in C too.
hex wrote:
Sun Jan 03, 2021 11:59
If you absolutely need to cripple a table with a sorting constraint, make a list of the keys and sort that. Wrap the whole thing in a container table, give it methods or hijack __index, even make your own iterator that spits out sorted pairs, and done.
I may be wrong but I've seen somewhere that one of the reason to take Lua as a mod language was it's simplicitly. Like if a mod could be easily written even by a kid. You don't tell kids to "hijack __index" for a such simple things as an ordered hashtable. This is spooky.
hex wrote:
Sun Jan 03, 2021 11:59
That said, I don't think I've ever needed a contraption like this. If not bundling it with the language keeps the Python clowns out, I'm more than fine with it.
That's a bit biased because I don't think I've ever needed an unordered hashtable either and consider it useless. Also if you'll keep all the clowns out you'll keep all the fun out too.

User avatar
jp
Banned
Posts: 947
Joined: Wed Dec 18, 2013 09:03
GitHub: kilbith
Location: France

Re: JavaScript for moddding

by jp » Post


User avatar
rubenwardy
Moderator
Posts: 6972
Joined: Tue Jun 12, 2012 18:11
GitHub: rubenwardy
IRC: rubenwardy
In-game: rubenwardy
Location: Bristol, United Kingdom
Contact:

Re: JavaScript for moddding

by rubenwardy » Post

Impressive how drkwv consistently has such bad takes
Renewed Tab (my browser add-on) | Donate | Mods | Minetest Modding Book

Hello profile reader

User avatar
Hume2
Member
Posts: 709
Joined: Tue Jun 19, 2018 08:24
GitHub: Hume2
In-game: Hume2
Location: Czech Republic

Re: JavaScript for moddding

by Hume2 » Post

drkwv wrote:
Sun Jan 03, 2021 12:44
I'm tired of these "speed" rants. You want speed -> write with Assembly language. You want more speed -> write bytecode. You want even more speed -> engineer an ASIC. You didn't grab a scripted language to gain speed. You need it to shorten the time you need to develop a program because your lifespan is short. That's the reason people invented scripted languages. So please doesn't mention speed. Lua is not about speed. It's 10^9x slower than ASIC microchip. Wouldn't it be great if minetest was an ASIC microchip and minetest mods were an ASIC microchips that you could attach to a minetest microchip??? Much speed would be gained then!
Have you ever heard of multicriterial optimisation?

Instead of explaining us why you don't like tables in Lua, tell us which existing mod would perform significantly better if it was written in JS instead of Lua.
If you lack the reality, go on a trip or find a job.

User avatar
rubenwardy
Moderator
Posts: 6972
Joined: Tue Jun 12, 2012 18:11
GitHub: rubenwardy
IRC: rubenwardy
In-game: rubenwardy
Location: Bristol, United Kingdom
Contact:

Re: JavaScript for moddding

by rubenwardy » Post

drkwv wrote:
Sun Jan 03, 2021 09:54
And also newest versions of Python and (sadly) Node.js do not support Windows 7. What a shame. Like if they had a share in Win10 sales forcing people to buy it. So you will have to stick with some old version of Python (and node) if you want to embed it. Because there is no real reason to drop OS support only because it's "old". Like if it suddenly became old overnight and everything broke in a moment.
Minetest does not support Windows 7. It's an unsupported operating system with unpatched vulnerabilities, it's not worth our limited time to fix problems on it. You may be able to run Minetest on it for now, but that doesn't mean it's supported
Renewed Tab (my browser add-on) | Donate | Mods | Minetest Modding Book

Hello profile reader

drkwv
Member
Posts: 102
Joined: Thu Jun 28, 2012 13:48
GitHub: aa6

Re: JavaScript for moddding

by drkwv » Post

rubenwardy wrote:
Sun Jan 03, 2021 15:26
Impressive how drkwv consistently has such bad takes
That's not even an argument. "I don't like your thoughts". Um, guess that's ok. I wish you a happy new year and stuff. Maybe someday you will see it at another angle and change your opinion. Probably my english is not good enough. I will work on that.
rubenwardy wrote:
Sun Jan 03, 2021 15:50
You may be able to run Minetest on it for now
That's the point. But if you'll try to install latest Python or Node on Windows 7 it will show you a "Go buy Windows 10!" message window with no another options.
Hume2 wrote:
Sun Jan 03, 2021 15:45
Have you ever heard of multicriterial optimisation?
No, I'm not. Let me google it. Ok. So we have a code simplicity and a code speed issue conflicting with each other. I choose first because the programmer's time, creativity and efforts are far more valuable than a new processor. Processor costs like 100$ and programmer's work published for free for everyone is priceless. You won't buy that at a computer store.
- Please sell me a bunch of new cool minetest mods.
- That will be 100$.
It doesn't work like that.
Hume2 wrote:
Sun Jan 03, 2021 15:45
Instead of explaining us why you don't like tables in Lua
I gave up on this thread a long time ago. It's just someone bumped it and I said that JavaScript is not cool anymore and people started arguing with me.
Hume2 wrote:
Sun Jan 03, 2021 15:45
tell us which existing mod would perform significantly better if it was written in JS instead of Lua.
I think any mod written on JS would have a better and elegant code and will be easier to understand and maintain. If you have a spare time (it's ok if not), consider writing the same code on Lua and we will compare it in terms of code simplicity, comprehensibility and maybe even speed performance although I don't think mods performance is the main issue for Minetest at this period of time.

Code: Select all

class Scoreboard 
{
    constructor()
    {
        this.data = {}
    }
    sort()
    {
        return this.data = [{}]
            .concat(Object.keys(this.data).sort((a,b) => {return this.data[b].rating-this.data[a].rating}))
            .reduce((acc,key) => { acc[key] = this.data[key]; return acc })
    }
    print()
    {
        console.log("*****")
        for(var key in this.sort()) { console.log(key,this.data[key]) }
        console.log("*****")
    }
    get_best_player()
    {
        return Object.entries(this.sort())[0]
    }
}

var scoreboard = new Scoreboard()
scoreboard.data["tom"] = { id:1, rating:100 }
scoreboard.data["jerry"] = { id:2, rating:200 }
scoreboard.data["he-man"] = { id:3, rating:1000 }
scoreboard.print()
scoreboard.data["jerry"].rating += 9000
scoreboard.data["somebody"] = { id: 56, rating: 400 }
delete scoreboard.data["tom"]
scoreboard.print()
console.log("Best player:", scoreboard.get_best_player())

drkwv
Member
Posts: 102
Joined: Thu Jun 28, 2012 13:48
GitHub: aa6

Re: JavaScript for moddding

by drkwv » Post

Although it would be cool if minetest had a shop where mod makers could sell their mods and support the core minetest development and infrastructure with a share of their revenue but I'll guess you won't like that idea either.

User avatar
v-rob
Developer
Posts: 970
Joined: Thu Mar 24, 2016 03:19
GitHub: v-rob
IRC: v-rob
Location: Right behind you.

Re: JavaScript for moddding

by v-rob » Post

drkwv wrote:
Sun Jan 03, 2021 09:54
And also newest versions of Python do not support Windows 7.
Perhaps a bit offtopic, but I find this kind of funny. If you really want to, you can compile Lua for DOS with something like Borland Turbo C and it will work just fine. That probably applies to quite a few languages written in C, perhaps even some JavaScript implementations. True, Python's more complicated since it has modules like Tkinter, but it's still funny that it doesn't even support Windows 7 and below. I mean, base programming languages should just need file/command line input and command line output on their host platform, i.e. standard C. Heh.
Core Developer | My Best Mods: Bridger - Slats - Stained Glass

User avatar
sorcerykid
Member
Posts: 1841
Joined: Fri Aug 26, 2016 15:36
GitHub: sorcerykid
In-game: Nemo
Location: Illinois, USA

Re: JavaScript for moddding

by sorcerykid » Post

Here's your Scoreboard class converted Lua. Seems pretty straightforward to me. Heck, I even improved this example by making the data structure private to the class along with methods to manipulate the data, which is how classes should be designed.

Code: Select all

function Scoreboard( )
	local self = { }

	-- Private Properties

	local data = { }

	-- Public Methods

	self.sort = function ( )
		local sorted_list = { }

		for k, v in pairs( data ) do
			table.insert( sorted_list, { name = k, rating = v } )
		end
		table.sort( sorted_list, function ( a, b ) return a.rating > b.rating end )

		return sorted_list
	end

	self.print = function ( )
		print( "*****" )
		for i, v in ipairs( self.sort( ) ) do
			 print( string.format( "#%-2d %-10s %6d", i, v.name, v.rating ) )
		end
		print( "*****" )
	end

	self.insert = function ( name, rating )
		assert( not data[ name ], "Entry already exists!" )

		data[ name ] = rating or 0
	end

	self.delete = function ( name, rating )
		assert( data[ name ], "Entry not found!" )

		data[ name ] = nil
	end

	self.set_rating = function ( name, rating )
		assert( data[ name ], "Entry not found!" )

		data[ name ] = rating
	end

	self.add_rating = function ( name, rating )
		assert( data[ name ], "Entry not found!" )

		data[ name ] = data[ name ] + rating
	end

	self.get_best_player = function ( )
		return self.sort( )[ 1 ].name
	end

	-- export object

	return self
end

local scoreboard = Scoreboard( )

scoreboard.insert( "tom", 100 )
scoreboard.insert( "jerry", 200 )
scoreboard.insert( "he-man", 1000 )
scoreboard.print( )

scoreboard.add_rating( "jerry", 9000 )
scoreboard.insert( "somebody", 400 )
scoreboard.delete( "tom" )
scoreboard.print( )

print( "Best player:", scoreboard.get_best_player( ) )

User avatar
duane
Member
Posts: 1715
Joined: Wed Aug 19, 2015 19:11
GitHub: duane-r
Location: Oklahoma City
Contact:

Re: JavaScript for moddding

by duane » Post

LOL

I cannot believe a necropost is inducing such angst. I do like how someone was dead sure we'd get a javascript port soon... six years ago.

Maybe we should have lisp support -- it's really good for AI (supposedly). How about RPGII? (Showing my age again.)

That's ok, part of being a "real programmer" is griping about how awful other programmers (and languages) are. Carry on. :)

v-rob wrote:
Sun Jan 03, 2021 23:54
drkwv wrote:
Sun Jan 03, 2021 09:54
And also newest versions of Python do not support Windows 7.
I mean, base programming languages should just need file/command line input and command line output on their host platform, i.e. standard C. Heh.
As much as I love python, and I do, part of what makes it great is the truly gigantic standard library, which you have to completely implement in any "real" python distribution. I can see why the developers wouldn't want to try to support more versions of windoze than they absolutely have to. That's a lot of code to adapt and keep running.
Believe in people and you don't need to believe anything else.

User avatar
Hume2
Member
Posts: 709
Joined: Tue Jun 19, 2018 08:24
GitHub: Hume2
In-game: Hume2
Location: Czech Republic

Re: JavaScript for moddding

by Hume2 » Post

drkwv wrote:
Sun Jan 03, 2021 16:09
No, I'm not. Let me google it. Ok. So we have a code simplicity and a code speed issue conflicting with each other. I choose first because the programmer's time, creativity and efforts are far more valuable than a new processor. Processor costs like 100$ and programmer's work published for free for everyone is priceless. You won't buy that at a computer store.
- Please sell me a bunch of new cool minetest mods.
- That will be 100$.
It doesn't work like that.
Do you have to buy a new processor each time you need to program anything? Definitely not, so it costs you only the electricity which it consumes, which isn't more than a few cents, which you can pay at your own. And also note that there are way more criteria like: how easy is to make an error, how easy is to make a security flaw, the effort needed to bind the scripting API into Minetest, how much it is being supported, how many useful libraries exist for that language, how much you like the way it copes with arrays ;), ... There are many more. And many of them are rather subjective so they can't be assigned any numerical value objectively. And also note that if one doesn't manage to make that working in a few hours for any reason, he may just pick another alternative. Simply, if there was a best language, all others would die.
If you lack the reality, go on a trip or find a job.

Oblomov
Member
Posts: 17
Joined: Fri Oct 30, 2020 23:06
GitHub: Oblomov
IRC: Oblomov
In-game: Oblomov

Re: JavaScript for moddding

by Oblomov » Post

Everybody here fighting about which is the best scripting language, and nobody mentions Ruby? What the heck is wrong with you people? ;-)

drkwv
Member
Posts: 102
Joined: Thu Jun 28, 2012 13:48
GitHub: aa6

Re: JavaScript for moddding

by drkwv » Post

sorcerykid wrote:
Mon Jan 04, 2021 03:46
Heck, I even improved this example by making the data structure private to the class along with methods to manipulate the data, which is how classes should be designed.
Since we are making a prototype I'd call it a "premature privatization" similar to "premature optimization". But ok.
sorcerykid wrote:
Mon Jan 04, 2021 03:46
data[ name ] = rating or 0
You throwed out an "id" property but that was actually needed. If you consider adding the "hp" and "mana" parameters alongside with a sort counter and printing the scoreboard every time the values are updating you will have to store the data and sorted_list separately which means:
1. Two entities (sorted_list,data) instead of one (this.#data).
2. Every time you have to manually reproduce the default algorithm that is already built-in into the JS hashtables.
Extrapolate that on ~15 entities and you will get 15+ more variables and ~60+ extra code lines doing the exact same job again and again that you need to write, check and maintain.

Code: Select all

class Scoreboard 
{
    #data = {}
    #sortcounter = 0
    sort()
    {
        console.log("Sorting... called " + (++this.#sortcounter) + " time(s)")
        return this.#data = [{}]
            .concat(Object.keys(this.#data).sort((a,b) => {return this.#data[b].rating-this.#data[a].rating}))
            .reduce((acc,key) => { acc[key] = this.#data[key]; return acc })
    }
    print()
    {
        console.log("*****")
        var counter = 0
        for(var key in this.#data)
        {
            console.log(
                ("#" + (counter++)).padEnd(4, " ") + key.padEnd(10," " ) + 
                this.#data[key].rating.toString().padStart(6," ") +
                this.#data[key].hp.toString().padStart(6," ") +
                this.#data[key].mana.toString().padStart(6," ")
            )
        }
        console.log("*****")
    }
    insert(name, obj)
    {
        if(this.#data[name] != null) { throw Error("Entry already exists!") }
        if(obj.hp == null) { obj.hp = 0 }
        if(obj.mana == null) { obj.mana = 0 }
        if(obj.rating == null) { obj.rating = 0 }
        this.#data[name] = obj
        this.sort()
        this.print()
    }
    delete(name)
    {
        if(this.#data[name] == null) { throw Error("Entry not found!") }
        delete this.#data[name]
        this.print()
    }
    add_hp(name,hp)
    {
        if(this.#data[name] == null) { throw Error("Entry not found!") }
        this.#data[name].hp += hp
        this.print()
    }
    add_mana(name,mana)
    {
        if(this.#data[name] == null) { throw Error("Entry not found!") }
        this.#data[name].mana += mana
        this.print()
    }
    add_rating(name,rating)
    {
        if(this.#data[name] == null) { throw Error("Entry not found!") }
        this.#data[name].rating += rating
        this.sort()
        this.print()
    }
    get_hp(name) 
    { 
        if(this.#data[name] == null) { throw Error("Entry not found!") }
        return this.#data[name].hp
    }
    get_mana(name) 
    { 
        if(this.#data[name] == null) { throw Error("Entry not found!") }
        return this.#data[name].mana
    }
    get_rating(name) 
    { 
        if(this.#data[name] == null) { throw Error("Entry not found!") }
        return this.#data[name].rating 
    }
    get_best_player()
    {
        return Object.entries(this.#data)[0]
    }
}

var scoreboard = new Scoreboard()
scoreboard.insert("tom",{ hp:1, mana: 100, rating:100 })
scoreboard.insert("jerry",{ hp:2, mana: 100, rating:200 })
scoreboard.insert("he-man",{ hp:10, mana: 300, rating:1000 })
scoreboard.add_rating("jerry",9000)
scoreboard.add_mana("jerry",600)
scoreboard.add_hp("jerry",100)
scoreboard.delete("tom")
scoreboard.insert("somebody",{ hp:90, mana: 120, rating: 400 })
console.log("Best player:", scoreboard.get_best_player())

User avatar
sorcerykid
Member
Posts: 1841
Joined: Fri Aug 26, 2016 15:36
GitHub: sorcerykid
In-game: Nemo
Location: Illinois, USA

Re: JavaScript for moddding

by sorcerykid » Post

1. Two entities (sorted_list,data) instead of one (this.#data).

In my view, that's the way it should be done. If I were to implement this scoring system in Perl, I would do it that way as well. Hash tables in many scripting language are not directly sortable. It's not is if there's significant overhead in storing a temporary list of keys. We're not running on an 8-bit CPU with only 64k of RAM.

2. Every time you have to manually reproduce the default algorithm that is already built-in into the JS hashtables.

That's hardly the case. I can easily create a table method to sort on any given field. And if performance is really that much of a concern, then a metatable could be used to cache the list of keys. Not sure why you think it has to be so overly-complicated. One of the beautiful qualities of Lua is the elegance in it's simplicity.

Code: Select all

------------------------------------------------------------
-- function table.sort_by( )
--
-- Sorts a table on the given numeric field and returns a
-- list of the sorted keys accordingly.
------------------------------------------------------------

function table.sort_by( data, field, is_reverse )
	local sorted_list = { }

	for k, v in pairs( data ) do
		table.insert( sorted_list, k )
	end
	table.sort( sorted_list, is_reverse and
		function ( a, b ) return data[ a ][ field ] > data[ b ][ field ] end or
		function ( a, b ) return data[ a ][ field ] < data[ b ][ field ] end
	)

	return sorted_list
end

function printf( str, ... )
	print( string.format( str, ... ) )
end

function Scoreboard( sort_field )
	local self = { }

	-- private properties

	local data = { }
	local sort_count = 0

	assert( ( { rating = true, hp = true, mana = true } )[ sort_field ], "Invalid field for sorting!" )

	-- public methods

	self.sort = function ( field )
		sort_count = sort_count + 1
		printf( "Sorting by %s... called %d time(s)", field or sort_field, sort_count )

		return table.sort_by( data, field or sort_field, true )
	end

	self.print = function ( )
		print( "*****" )
		printf( "%-4s %-10s %6s %6s %6s", "RANK", "NAME", "RATING", "HP", "MANA" )
		for i, v in ipairs( self.sort( ) ) do
			 printf( "#%-3d %-10s %6d %6d %6d", i, v,
				data[ v ].rating, data[ v ].hp, data[ v ].mana )
		end
		print( "*****" )
	end

	self.insert = function ( name, def )
		assert( not data[ name ], "Entry already exists!" )

		data[ name ] = {
			rating = def.rating or 0,
			hp = def.hp or 0,
			mana = def.mana or 0,
		}
		self.print( )
	end

	self.delete = function ( name, rating )
		assert( data[ name ], "Entry not found!" )

		data[ name ] = nil
		self.print( )
	end

	self.add_rating = function ( name, rating )
		assert( data[ name ], "Entry not found!" )

		data[ name ].rating = data[ name ].rating + rating
	end

	self.add_mana = function ( name, mana )
		assert( data[ name ], "Entry not found!" )

		data[ name ].mana = data[ name ].mana + mana
	end

	self.add_hp = function ( name, hp )
		assert( data[ name ], "Entry not found!" )

		data[ name ].hp = data[ name ].hp + hp
	end

	self.get_best_player = function ( field )
		return self.sort( field )[ 1 ]
	end

	-- export object

	return self
end

local scoreboard = Scoreboard( "rating" )

scoreboard.insert( "tom", { hp = 1, mana = 100, rating = 100 } )
scoreboard.insert( "jerry", { hp = 2, mana = 100, rating = 200 } )
scoreboard.insert( "he-man", { hp = 10, mana = 300, rating = 1000 } )
scoreboard.add_rating( "jerry", 9000 )
scoreboard.add_mana( "tom", 600 )
scoreboard.add_hp( "he-man", 100 )
scoreboard.delete( "tom" )
scoreboard.insert( "somebody", { hp = 90, mana = 320, rating = 400 } )

print( "Best player by rating:", scoreboard.get_best_player( "rating" ) )
print( "Best player by hp:", scoreboard.get_best_player( "hp" ) )
print( "Best player by mana:", scoreboard.get_best_player( "mana" ) )

drkwv
Member
Posts: 102
Joined: Thu Jun 28, 2012 13:48
GitHub: aa6

Re: JavaScript for moddding

by drkwv » Post

sorcerykid wrote:
Mon Jan 04, 2021 16:31
In my view, that's the way it should be done. If I were to implement this scoring system in Perl, I would do it that way as well. Hash tables in many scripting language are not directly sortable. It's not is if there's significant overhead in storing a temporary list of keys. We're not running on an 8-bit CPU with only 64k of RAM.
Well that's I disagree with. It's ok when you have 1 field but complicates the code when you keep an index field for each data list. Not talking about speed etc. Also every time you addressing the data you start addressing the index instead of the data object itself (you write "for k, v in data_index" instead of "for k, v in data"). That makes code less clear.
sorcerykid wrote:
Mon Jan 04, 2021 16:31
I can easily create a table method to sort on any given field. And if performance is really that much of a concern, then a metatable could be used to cache the list of keys.
I could also extend Object.prototype with my own implementation of sort method but in JS it is considered a bad practice. Also I'm not sure what will happen to the code if another mod maker will decide to add his own function table.sort_by... implementation. Probably it will break. Also you will need to manually implement that cache and you will need to do it every time you dealing with a sorted data (when you have more than one column) - that's exactly what I'm talking about. Unnecessary, uninteresting work just because Lua authors were too lazy to implement it.

Just to be accurate - as for now your code doesn't print rating after HP and MANA change. Print is intended to be used as some sort of scoreboard update function.

Code: Select all

class Scoreboard 
{
    #data = {}
    #sort_field
    #sort_counter = 0
    constructor(sort_field)
    {
        this.#sort_field = sort_field
    }
    sort(field = this.#sort_field)
    {
        console.log("Sorting by %s... called %d time(s)", field, ++this.#sort_counter)
        return this.#data = [{}]
            .concat(Object.keys(this.#data).sort((a,b) => {return this.#data[b][field]-this.#data[a][field]}))
            .reduce((acc,key) => { acc[key] = this.#data[key]; return acc })
    }
    print()
    {
        console.log("*****")
        console.log([
            "RANK".padEnd(4, " "),"NAME".padEnd(10," " ),"RATING".padStart(6," "),
            "HP".padStart(6," "),"MANA".padStart(6," "),
        ].join(" "))
        var counter = 0
        for(var key in this.#data)
        {
            console.log([
                ("#" + (counter++)).padEnd(4, " "), key.padEnd(10," " ),
                this.#data[key].rating.toString().padStart(6," "),
                this.#data[key].hp.toString().padStart(6," "),
                this.#data[key].mana.toString().padStart(6," "),
            ].join(" "))
        }
        console.log("*****")
    }
    insert(name, obj)
    {
        if(this.#data[name] != null) { throw Error("Entry already exists!") }
        if(obj.hp == null) { obj.hp = 0 }
        if(obj.mana == null) { obj.mana = 0 }
        if(obj.rating == null) { obj.rating = 0 }
        this.#data[name] = obj
        this.sort()
        this.print()
    }
    delete(name)
    {
        if(this.#data[name] == null) { throw Error("Entry not found!") }
        delete this.#data[name]
        this.print()
    }
    add_hp(name,hp)
    {
        if(this.#data[name] == null) { throw Error("Entry not found!") }
        this.#data[name].hp += hp
        this.print()
    }
    add_mana(name,mana)
    {
        if(this.#data[name] == null) { throw Error("Entry not found!") }
        this.#data[name].mana += mana
        this.print()
    }
    add_rating(name,rating)
    {
        if(this.#data[name] == null) { throw Error("Entry not found!") }
        this.#data[name].rating += rating
        this.sort()
        this.print()
    }
    get_hp(name) 
    { 
        if(this.#data[name] == null) { throw Error("Entry not found!") }
        return this.#data[name].hp
    }
    get_mana(name) 
    { 
        if(this.#data[name] == null) { throw Error("Entry not found!") }
        return this.#data[name].mana
    }
    get_rating(name) 
    { 
        if(this.#data[name] == null) { throw Error("Entry not found!") }
        return this.#data[name].rating 
    }
    get_best_player(field)
    {
        this.sort(field)
        return Object.entries(this.#data)[0]
    }
}

var scoreboard = new Scoreboard("rating")
scoreboard.insert("tom",{ hp:1, mana: 100, rating:100 })
scoreboard.insert("jerry",{ hp:2, mana: 100, rating:200 })
scoreboard.insert("he-man",{ hp:10, mana: 300, rating:1000 })
scoreboard.add_rating("jerry",9000)
scoreboard.add_mana("tom",600)
scoreboard.add_hp("he-man",100)
scoreboard.delete("tom")
scoreboard.insert("somebody",{ hp:90, mana: 320, rating: 400 })
console.log("Best player by rating:", scoreboard.get_best_player("rating"))
console.log("Best player by hp:", scoreboard.get_best_player("hp"))
console.log("Best player by mana:", scoreboard.get_best_player("mana"))

User avatar
sorcerykid
Member
Posts: 1841
Joined: Fri Aug 26, 2016 15:36
GitHub: sorcerykid
In-game: Nemo
Location: Illinois, USA

Re: JavaScript for moddding

by sorcerykid » Post

Well that's I disagree with. It's ok when you have 1 field but complicates the code when you keep an index field for each data list.
Except you don't have to keep an index field for each data list. The data structure is always indexed by player name, so the only list to cache (if you decide to go that route) is the player names. If you want to sort on a different field, then you just cache the list of player names sorted according to that field. It's not difficult.

Here's a diff that shows the required changes to add caching as described.

Code: Select all

29c29
< 	-- private properties
---
> 	-- private properties and methods
31a32
> 	local sorted_list
36,38c37
< 	-- public methods
< 
< 	self.sort = function ( field )
---
> 	local function cached_sort( )
42c41
< 		return table.sort_by( data, field or sort_field, true )
---
> 		sorted_list = table.sort_by( data, sort_field, true )
44a44,45
> 	-- public methods
> 
48c49
< 		for i, v in ipairs( self.sort( ) ) do
---
> 		for i, v in ipairs( sorted_list ) do
62a64
> 		cached_sort( )
69a72
> 		cached_sort( )
75a79
> 		if sort_field == "rating" then cached_sort( ) end
81a86
> 		if sort_field == "mana" then cached_sort( ) end
87a93
> 		if sort_field == "hp" then cached_sort( ) end
92c98,100
< 		return self.sort( field )[ 1 ]
---
> 		return field and field ~= sort_field and
> 			 table.sort_by( data, field, true )[ 1 ] or
> 			 sorted_list[ 1 ]
Also every time you addressing the data you start addressing the index instead of the data object itself (you write "for k, v in data_index" instead of "for k, v in data"). That makes code less clear.
The purpose of an index is to index. If that "makes code less clear" to you, then I think programming may not be your forte. If you really want to talk about code clarity let's consider this monstrosity:

Code: Select all

        return this.#data = [{}]
            .concat(Object.keys(this.#data).sort((a,b) => {return this.#data[b][field]-this.#data[a][field]}))
            .reduce((acc,key) => { acc[key] = this.#data[key]; return acc })
Just from a cursory glance, that looks like spaghetti code with all kinds of obtuse JS-specific notations. It gives me a headache to even attempt to parse what you are doing here. It's hilarious that you are the one complaining about code obfuscation, whereas the Lua version is extremely straightforward and easy to comprehend:

Code: Select all

        local sorted_list = { }
        for k, v in pairs( data ) do
                table.insert( sorted_list, k )
        end
        table.sort( sorted_list, function ( a, b )
                return data[ a ][ field ] > data[ b ][ field ]
        end )
Hmm, yeah I'll take the Lua version over your spaghetti JS code any day. As a general principle of ALL scripting languages, readability should take priority over efficiency-by-complexity.
I could also extend Object.prototype with my own implementation of sort method but in JS it is considered a bad practice.
Lua was designed originally as a configuration language and then adapted as an embedded scripting language. So it's considered perfectly acceptable to extend core objects. The example I gave of a table sort function was thus to be included as a separate library, not implemented directly within a mod. Regardless, you could always just make a helper function local to the mod to avoid a conflict altogether. It's not that big of a deal.
Also you will need to manually implement that cache and you will need to do it every time you dealing with a sorted data (when you have more than one column) - that's exactly what I'm talking about.
You are focusing too much on a single implementation detail. Also you already admitted that speed is not a concern, so a cache isn't needed anyway. It sounds as if you are determined to find anything to complain about to prop up your worldview that JS is the only acceptable option.

As someone that's come from a background of many different programming languages (6502 assembly, 8088 assembly, 68000 assembly, ANSI C, Pascal, Perl, Awk, ECMAScript, VisualBasic, OpenScript, etc.), I'm perfectly content with what Lua has to offer for the purposes of game scripting.

Post Reply

Who is online

Users browsing this forum: No registered users and 8 guests