I open sourced my game along with a tutorial on how to make it using Lua
github.com
github.com
https://github.com/a327ex/blog/issues/35 - BYTEPATH Postmortem (2018)
https://github.com/a327ex/blog/issues/44 - One Year Sales Data for BYTEPATH (2019)
Lua is a language for both hardcore C programmers and complete n00bs wanting to configure their C programs. In that context, position-based indexing makes much more sense, as it's how most non-programmers think.
Like a string will always be:
s:sub(1, #s)
And a sequence table will always be: for i = 1, #t do
t[i]
end function split_dot(s)
local idx = s:find(".", 1, true)
if not idx then return s end
return s:sub(1,idx-1), s:sub(idx+1)
end
It may come as a surprise that one should use a difference of 2 to skip over one character!In a language that used the half-open interval convention we would end up with:
s:sub(0, #s)
and for i = 0, #t do
t[i]
end
which seem just as good to me. s:sub(0, #s)
You would end up with: s:sub(0, #s - 1)
Which demonstrates the off-by-one error.In python, for example, the operation of copying a list can be correctly written l[0:len(l)]. You're more likely to see l[::] or list(l) though.
Edit: I think maybe the proposed error that's being avoided is "a user tries to use a closed interval when sub takes a half-open interval." It seems like the opposite error is similarly likely though. Some areas where it's nice to have half-open intervals like using a modulus to restrict a value to an interval or parsing strings don't have such a symmetry.
Notably if you're making a binary heap the formulas come out much nicer with 1-indexing than with 0-indexing, but I haven't met any other examples where this is the case.
Lua does do that, so in this theoretical world with 0-indexed strings, it would still work as expected.
But it doesn't change the fact that you're not actually specifying the right length - the language is correcting an off-by-one error, which means that the programmer's mental model is probably going to be wrong, and it will bite them when they don't expect it to.
That's what off-by-one errors are - a place where the programmer's mental model doesn't actually fit the real world. It is better to not introduce places where they can happen in the first place, rather than to complicate things with edge cases where sometimes it works and sometimes it doesn't.
No, it's not-correcting the not-error of the programmer specifying the correct upper bound using the half-open-interval model, a common mathematical model of range specification that has lots of useful properties.
s = "foo"
s.length
#=> 3
s[0, s.length]
#=> "foo"
Same in JavaScript s.slice(0, s.length)
and Python s[0:len(s)]To get the last object of an array in JS is:
arr = [1, 2, 3]
arr[arr.length - 1]
To get the last object of an array in Python is: arr = [1, 2, 3]
arr[len(arr) - 1]
To get the last object of an array in Lua is: arr = {1, 2, 3}
arr[#arr]> non-string objects
There is no difference between array and string access in ruby, python, js.
There are better ways to access last element (python, ruby):
>>> arr[-1] # meaning first element from back (1-indexing!)
3
I mentioned your comment is wrong. I do not know why you are turning it into 0-indexing vs 1-indexing, looks like changing topic for me.[1] https://en.wikipedia.org/wiki/Comparison_of_programming_lang...
... That's exactly where this topic started:
> 1-index kept throwing me off though
---
> There is no difference between array and string access in ruby, python, js.
There's no difference in Lua either:
s:sub(1, 1)
tbl:select(1, 1)
But it doesn't use offset, and I find that more useful for preventing off-by-one errors.>>>> a string will always be:
s:sub(1, #s)
>>> In a language that used the half-open interval convention we would end up with: s:sub(0, #s)
>> You would end up with: s:sub(0, #s - 1)
> That would be strange, in Ruby s[0, s.length]
Your argument against 0-index is just plain wrong. No one creates such API. It would be to awkward - either #s should be `length + 1` or `sub` should expect `length + 1`.----
>>>> Once you get used to it, ... you don't need math when grabbing a range.
I assure you once you get used to 0-indexing there is no need for math when grabbing a range
s[0..s.length] # that is identity through range in ruby
---->>>>> 1-index kept throwing me off though
>>>> it does help prevent a whole bunch of off by one errors
You claim superiority, it is not. I can't remember such errors in my life. It may be easier to describe to children but I doubt it. My first encounter with 0-indexing was with ruler - "why it starts with 0?" - pre-school. I can claim 0-index is natural. I would not.
There is no better model just "happy accidents".
On the contrary; unless you're very used to offset-indexing, it seems more natural to start at the next element for the next substring, or the 2nd next element when skipping one.
Sure it may not be the most critical point about language design, but it's not just bikeshedding either. For one thing, it is a language semantics issue rather than plainly syntax issue.
I still like Lua, and maybe you don't even agree with Dijkstra's argument, which is also possible to make, but doesn't mean its bikeshedding - which seems to be a totally overused term to basically mean any argument I don't want to have.
Just kidding, I actually agree with your point that the term is just demonstrate that you don’t want to have a conversation by diminishing the value of anybody having it.
> When we started Lua, the world was different, not everything was C-like. Java and JavaScript did not exist, Python was in an infancy and had a lower than 1.0 version. So there was not this thing when all the languages are supposed to be C-like. C was just one of many syntaxes around.
> And the arrays were exactly the same. It’s very funny that most people don’t realize that. There are good things about zero-based arrays as well as one-based arrays.
> The fact is that most popular languages today are zero-based because of C. They were kind of inspired by C. And the funny thing is that C doesn’t have indexing. So you can’t say that C indexes arrays from zero, because there is no indexing operation. C has pointer arithmetic, so zero in C is not an index, it’s an offset. And as an offset, it must be a zero — not because it has better mathematical properties or because it’s more natural, whatever.
> And all those languages that copied C, they do have indexes and don’t have pointer arithmetic. Java, JavaScript, etc., etc. — none of them have pointer arithmetic. So they just copied the zero, but it’s a completely different operation. They put zero for no reason at all — it’s like a cargo cult.
(Bit of a tangent: Lisp predates C by at most 2 years if you count McCarthy's original 1960 paper, but afaict their respective implementations both got their first public distribution in 1962. Of course, Lisp was set to gain momentum from that time until the AI winter, while C was practically confined to UNIX until the 80s...)
In early computing, assembly language saw a lot of use. The navigation program that sent the Apollo mission to the Moon was written in assembly language. During the micro-computer boom, which echoed the history of big iron boom, a lot of applications and systems were written in assembly language again. Most commercial video games for 8 bit micros were written in assembly language. The famous WordPerfect word processor for the IBM PC was assembly language. The VMS operating system: assembly language.
C became easily popular because it gave a nice notation, and a sprinkling of type, to assembly language memory manipulation concepts that most professional programmers already knew how to use.
In the end, in a language where you aren't doing pointer arithmetic, the sides are pretty even. We just tend to side with tradition, because it's easier to.
y = margin + (i - 1) * lineheight y = 1 + ...
if y is 1-based too (which feels even more uncomfortable). But it would be nice to have such functions out if the box: canvas.linear_index(x, y, stride)
vbox.y1_for(margin, spacing, height, i)
vbox.y2_for(margin, spacing, height, i)
And then you have "integer at the center of a pixel" vs "integer at the top-left of a pixel" differences, which should be taken into account by these functions too. Stroke 1: y+0.5, y+height-1. Stroke 2: y+1, y+height-2. Fill: y, y+height.Pixel geometry is hard on its own.
0/1-based are simply two degenerate around-off-by-one cases of working at general n-based offset, since both 0 and 1 are the most common results of off-by-one.
Agreed. Here is an example of exactly that in Python:
Lua, both has a dedicated nil value and a boolean type, so there's no reason whatsoever to treat zero as falsey, other than the C cargocult.
As for empty strings, I really don't see the rationale behind it. An empty string is not nothing. At the very least, it still holds the length-data of 0, so it's conceptually more than just nil and should thus still be truthy.
Python does this, which leads to people checking whether a list is empty using `if l` rather than `if len(l) > 0`. I‘d rather they stuck with “explicit is better than implicit”.
One of the easiest to code cross platform development languages there is.
https://www.wowhead.com/guide=5338/comprehensive-beginners-g...
SQL, Lua, C++, and HTML all around 2006 to host private World of WarCraft servers.
Great talking point at my previous interviews.
https://www.lexaloffle.com/pico-8.php
Celeste, a successful commercial game, was prototyped in Pico-8
Of the three I thought Defold was the best and was supported by a company of commercial game developers. If I were going to start today it would be my top one to consider.
Don't let that stop you using the software of course, as long as you have knowledge of the actual license being used.
A new text editor in Lua, why not: https://github.com/rxi/lite
(Rxi is awesome, see https://rxi.itch.io/)
curl -sI https://itch.io | grep -i server
Server: lapis[1]: https://www.planimeter.org/grid-sdk/ [2]: https://github.com/topics/love2d
Additionally, adnzzzzZ and I are both from the Facepunch/Knockout community, where a lot of Lua developers have come from due to its popularity in Garry's Mod. It also had a wonderful web dev community.
There are a lot of cool developers from that community, like Gran PC who works at itch.io and worked on The Stanley Parable, and TheBerkin who designed rant, among many other highly skilled individuals, though most people at HN are probably more familiar with developers like sebmck who wrote 6to5, which later became Babel, and Gabriele Cirulli who wrote 2048.
It is written in Rust and aims to be 100% hot-reloadable.
As you mention, Roblox is actually a huge platform for creating games in Lua, and helping other players discover them. Not only do you get a cross-platform game engine with multiplayer built in, it also seems to be quite pleasant to develop for.
For instance, you can create both client-side and server-side Lua in the same IDE, and spin up a local server with a client that connects to it in a single click with no configuration. You can also add breakpoints and step through both sides in the debugger. Pretty awesome as I was used to client-side JavaScript and server-side Python before.
Because the platform encourages experimentation, I think we'll see many new game ideas and probably even new genres emerging from Roblox.
It's always interesting to see games published made with LÖVE in lua. I'm writing an "Advance Wars 2" clone at the moment in LÖVE and hope to get some inspiration from your code.
You seem to use a lot of global variables. Is that on purpose? I try to do as much as possible with local variables (although especially in game logic global entities are sometimes just the easiest way). I'm very wary of 1. global lookup overhead and 2. confusing errors when accidentally overwriting an existing global variable.
As I have never noticed both of these I'm not sure whether my angst is justified ;-)
https://github.com/a327ex/blog/issues/24
It mainly boils down to the fact that an indie game written by one person and never maintained after release doesn't need to be written for maintainability. Supposedly there are productivity gains the author receives from doing this.
Personally, I disagree from past experiences dealing with hundreds of globals in legacy code, but to each their own.
I see the point that a single developer does not have to focus on maintainability so much. And if it works for him -- as it obviously does -- more power to him.
But for me, I do not think I am mentally capable to reason about my program if it has to many globals. If I can have as much as possible local, my mental model also can be local.
I am very new to gamedev, so this way of thinking may not be appropriate in the long run.
A rich git history a lot of the times imo is necessary to understand why some parts are written the way they are written, and is also enjoyable to read.
Also, are you using moonscript and SDL2 in this?
Great project by the way! The pixellated graphic effects are clever and unique.
When I packaged a game for Mac it was a bit more painful but not too bad, and I told Linux users to use wine. Some people have made it pretty easy to package love games for Linux since then though.
(PoE is a awesome ARPG with a massive build customization and I believe inspired game creates by OP)