ID:120338
 
When you first start programming, the problem you're trying to solve is "how do I write code that'll do ______?". Initially you're happy just to get something working, who cares what the code looks like. But when you try to develop a complete game this indifference can become a problem. As your code gets messier and messier, the project becomes harder to work on - you're more likely to write code that has bugs and they become harder to track down and fix. Eventually it'll get to the point where you're wasting a lot of time, don't feel productive, and are more likely to stop working on the project.

As a more experienced programmer, instead of asking "how do I do ______?", you should start asking "what's the best way to do ______?". In this post we'll look at different ways to write a simple function to see how certain ways are better than others.


Suppose you have a simple task - you want to find a target for the mob to attack. There are certain conditions we need to take into account. You only target living mobs. You don't target friendly mobs (mobs on your team). And you only target mobs that are more than 4 tiles away. Here's how you might implement this:

for(var/mob/m in oview(10,src))
if(m.alive && m.team != team && get_dist(src, m) > 4)
target = m
break

It's looking kind of messy already but it's still manageable. If you add more to this process it's easy for things to get out of hand. Suppose we create mobs that aren't assigned a team. If a mob isn't on a team it's hostile to everyone. This complicates the check because it's not just enough to check that the mob's not on your team, if both of your team vars are set to NONE they're still a valid target.

for(var/mob/m in oview(10,src))
if(m.alive && (m.team != team || m.team == NONE) && get_dist(src, m) > 4)
target = m
break

Now it's getting messy. You might be thinking "Big deal, I'm not a noob. I'm a good programmer, I can handle a complex conditional statement like that!", but here are some problems you may run into:

1. Don't look at this code for two weeks, then come back to it - it won't be obvious what is happening.
2. If you add more and more conditions to the same if statement you'll eventually lose track of parenthesis and make the code incorrect.

As you learn and become a better programmer you will get better at reading code. But, you'll never get good enough that you read statements like this and understand them instantly. Good programmers don't effortlessly make sense of complex code, they know how to write simple-looking code to do the same thing.

Let's look at one way to make this simpler. You can split up each part of the condition into a separate if statement:

for(var/mob/m in oview(10,src))
if(m.alive)
if(m.team != team)
if(get_dist(src, m) > 4)
target = m
break

Each individual if statement is easy to see, but that much indentation still makes it hard to read. Here's another way to make it more readable:

for(var/mob/m in oview(10,src))
if(!m.alive) continue
if(m.team == team) continue
if(get_dist(src, m) <= 4) continue

target = m
break

The continue statement skips the rest of the current iteration of the for loop and starts the next iteration. We check the opposite of each condition - instead of checking for conditions a valid target must satisfy, we check for conditions that make the target invalid. If any condition makes the mob an invalid target we skip the iteration and look at the next mob.

Another way you can simplify complex conditionals is to store them in local variables. For example:

for(var/mob/m in oview(10,src))
var/too_close = get_dist(src, m) <= 4
var/friendly = m.team == team && m.team != NONE

if(m.alive && !too_close && !friendly)
target = m
break

It's easy to see that the if statement is checking that the mob is alive, not too close, and not friendly. You keep all the complexity in the local variables so the condition is easy to read. There's not a single comment you can put in that code to make it clearer what's happening, the variable names and simplified if statement serve as the documentation.
"what's the best way to do ______?" is what I've always gone for, but then I run into the issue of not knowing what is the best way and getting hung up on small details.
I automatically split things into small steps because I'm a perfectionist and like it to look nice lol.
The weird thing is, I'm a terrible programmer but I've always for some reason programmed in the way you just suggested. I was never fond of piling tons of conditional statements like that. I always did each if statement one after another, and assumed that the first example is what more advanced programmers did ( because it looks a bit more complex ).
EmpirezTeam wrote:
I always did each if statement one after another, and assumed that the first example is what more advanced programmers did ( because it looks a bit more complex ).


LordAndrew wrote:
"what's the best way to do ______?" is what I've always gone for, but then I run into the issue of not knowing what is the best way and getting hung up on small details.

Can you give some examples? I'd like to make more posts like this and these issues might give me ideas... or I might get hung up on them too.

In general, I'd say to just write the code whatever way you're comfortable with if you can't easily figure out what the best approach is. As long as you're aware that there might be a better way to do things and you might have to switch later. If you know you might have to change the code around later, it's important to follow these lower-level details. The cleaner the code is, the easier it'll be to change around later.
EmpirezTeam wrote:
I always did each if statement one after another, and assumed that the first example is what more advanced programmers did ( because it looks a bit more complex ).

I would bet that most BYOND programmers write it the complex way. People assume that programming is really hard so they don't even try to look for some obvious ways to make it easy. It's like they try to make it hard for themselves so they feel more accomplished. If the code was really simple and easy to read it wouldn't be impressive to look at and think "yeah, I'm awesome, I know what that means".

I'd also bet that most DM programmers would write it in a single if statement because they think it's faster and that having two or three if statements will make their game lag. The best part of this irrational fear of lag is that if you write messy code that's "fast", you're more likely to never finish the project - who cares about performance if the game is never finished! =)
When I was a nub, I was programming the way you suggested. Also, no offense to people who use it. After a while of experience, I started to grow my own style. My own style combines up to 6 if statements in one ;/
Forum_account wrote:
LordAndrew wrote:
"what's the best way to do ______?" is what I've always gone for, but then I run into the issue of not knowing what is the best way and getting hung up on small details.

Can you give some examples? I'd like to make more posts like this and these issues might give me ideas... or I might get hung up on them too.

In general, I'd say to just write the code whatever way you're comfortable with if you can't easily figure out what the best approach is. As long as you're aware that there might be a better way to do things and you might have to switch later. If you know you might have to change the code around later, it's important to follow these lower-level details. The cleaner the code is, the easier it'll be to change around later.

Mostly minor things like if using += or the Add() proc is better with lists, to things like "should I make classes their own mob type (/mob/knight, /mob/mage) or attach a datum to mobs and handle all that data" and such. I'm really indecisive about which way to go about doing things.
That's something I never got and never took the time to figure out, What the hell is a datum???
This is very nice. Thank you, F_A!
GreatFisher wrote:
That's something I never got and never took the time to figure out, What the hell is a datum???

^. Also, how and why are they used for classes? I never used datums or give classes their own mob types, I simply use a variable to determine what class people are. I'm in a mind-bending world of confusion right now.
EmpirezTeam wrote:
GreatFisher wrote:
That's something I never got and never took the time to figure out, What the hell is a datum???

^. Also, how and why are they used for classes? I never used datums or give classes their own mob types, I simply use a variable to determine what class people are. I'm in a mind-bending world of confusion right now.

LordAndrew wrote:
Mostly minor things like if using += or the Add() proc is better with lists, to things like "should I make classes their own mob type (/mob/knight, /mob/mage) or attach a datum to mobs and handle all that data" and such. I'm really indecisive about which way to go about doing things.

It can be hard to figure out what problems you might run into. Just take a guess. For example, make all classes use the same base /mob type and if you run into a problem, think "would I have had this problem if each class was its own type?"

Most of the time you won't run into any huge problems, so I wouldn't bother spending too much time trying to figure out what way is best. Write code so it's easy to change and use trial and error.
I generally think from my experience and as a newby programmer, you do not learn about adding multiple effects into one code line, until Much later on.

This is generally how the dm guide teaches you.
for(var/mob/m in oview(10,src))
if(m.alive)
if(m.team != team)
if(get_dist(src, m) > 4)
target = m
break
EmpirezTeam wrote:
GreatFisher wrote:
That's something I never got and never took the time to figure out, What the hell is a datum???

^. Also, how and why are they used for classes? I never used datums or give classes their own mob types, I simply use a variable to determine what class people are. I'm in a mind-bending world of confusion right now.

It really depends on the game.

For classes, I can imagine a datum being used in a case where your character can switch between classes (I think there are some FF games like this). Each mob would have a datum for each class. The datum would store what level you are and what abilities you have for a single class. Because it's a datum you can easily create many of them for a single player.

You can use different mob types to keep things simple by overriding procs:

mob
proc
attack()

knight
attack()
world << "[src] attacks like a knight!"

mage
attack()
world << "[src] attacks like a mage!"
That's kinda helpful but what is a datum.
GreatFisher wrote:
That's kinda helpful but what is a datum.

It's just a custom object you can use to store information. For character classes you can use it to bundle related variables:

// instead of doing this:
mob
var
class
experience
level
list/abilities

// you can do this:
Class
var
name
experience
level
list/abilities

mob
var
Class/class


That way if you wanted to change the mob's class, you just change the datum.
Please keep your comments friendly. The DM reference doesn't explain what you can do with a datum. Learning how to use them effectively isn't as easy pressing F1.

Every programming problem can be solved by defining and manipulating variables. The DM reference explains how to create and manipulate variables, but it certainly doesn't explain how to solve all problems.
Hey F A could you give me a hand with implementing your visibility group library? If so I'll give you more info.

Actually after reading a bit I think I got it.
Page: 1 2