A lot of times when writing code you will have something that doesn't error but doesn't work either. These, very simply, are bugs in your code.
TL;DR: When your code isn't working but you don't know why, add
print() statements to every unique code block and figure out exactly which lines of code are and, more importantly, are not running! Use these
print()s to check what your variables are as well.
This article will be about how to use
print() as your most basic tool for "debugging" your code!
Bugs in Code
Most of the time, bugs happen because your code's "logic" is incorrect or inconsistent. Code "logic", or the "flow" of a program, determines what specific statements run and which do not.
The most basic "logical operation", typically called a control statement is the
if someExpression then print("it's true!") elseif someOtherExpression then print("It wasn't but something else was!") else print("neither were true!") end
Based on the values of
someOtherExpression, only one of those three
print() statements will run, along with any other code in those blocks.
Where debugging with
print() comes in is when the logic isn't working like you think it should be. We all write code that we think works, and something proves us wrong, so we go back and change it. Figuring out what to change is an important and sometimes difficult process.
Take, for example, this
if statement which is part of checking if a move is valid in chess:
-- to is the square the piece is moving to -- from is the square the piece is moving from if from.HasPiece and to.HasPiece and from.Piece.Color ~= to.Piece.Color then -- capture piece elseif from.Piece.Color == to.Piece.Color then -- invalid move, throw an error return else -- move piece to open square end
A savvy programmer will notice a few bugs in that code without needing to write any debug
print()s, but can you?
While testing the chess game, you notice that you can't actually move pieces to open spaces! Instead, you get an error:
attempt to index 'Piece', a nil value, on line 5! If you go a little further, you might also notice that you get the same error if you try to move a piece from an empty tile, no matter what the target tile is!
elseif from.Piece.Color == to.Piece.Color then shouldn't be erroring, you think. But it is, and you know that either
to.Piece don't exist when this line runs in those two known conditions. Again, a savvy programming would notice immediately what the problem is, but let's assume (or for some of you this might still be true!) you don't know what's going on.
Rather than make random edits to that line to try and get the code working, we first need to figure out what is working. One of the most useful ways to do this is
print() statements holding some debug info.
Specifically, we should start by adding them to every unique code block:
print("code is running") print("from.HasPiece", from.HasPiece) print("to.HasPiece", to.HasPiece) if from.HasPiece and to.HasPiece and from.Piece.Color ~= to.Piece.Color then -- capture piece print("Capturing piece" .. to.Piece.Name) elseif from.Piece.Color == to.Piece.Color then print("Invalid move!") -- invalid move, throw an error return else print("Moving piece") -- move piece to open square end
The point of this is two-fold: following the computer as it executes the code, and checking the values of our variables at various points in the code.
For something this simple, all we do is validate what we know:
Capturing piece prints correctly,
Invalid move! prints when trying to move pieces of the same color on top of each other, but the same line errors, the
elseif, when trying to move a piece.
However, we also see the values of
to.HasPiece! The line only errors when one or both of them is
Since we know Capturing works fine, we can remove that
print() entirely. However, we will leave the other two.
Moving piece because it does not fire at all, the variable prints because our error only happens based on their values and
Invalid move! because, for some reason, it is the branch that is erroring.
This process of removing the prints as you verify what code is working until you get down to only a few that don't work correctly should be after putting them in in the first place. If you don't have enough prints, you won't be able to see what the code is doing and will be no closer to fixing the problem. There's no fee for adding or removing them, so use what looks like too many to start with and remove them as you narrow down the bug!
Fixing the Bugs
Look closely at the line that is erroring:
elseif from.Piece.Color == to.Piece.Color then
print()s we added, we know that this only errors when
HasPiece is false, meaning that
nil for that square (either
And this makes perfect sense! The very first thing we try to do is check a property of
Piece! Additionally, when we're trying to move a piece, it's actually
to.Piece erroring, for the same reason!
You may have written the code thinking that this
elseif wouldn't run if the original
if statement found that the pieces didn't exist, because we do check for it there, or you may simply have forgotten to check. As you can see, assumptions aren't always correct!
By adding those two checks to the
elseif expression, all of the prints are now working!
print("from.HasPiece", from.HasPiece) print("to.HasPiece", to.HasPiece) if from.HasPiece and to.HasPiece and from.Piece.Color ~= to.Piece.Color then -- capture piece elseif from.HasPiece and to.HasPiece and from.Piece.Color == to.Piece.Color then print("Invalid move!") -- invalid move, throw an error return else print("Moving piece") -- move piece to open square end
However, they're not correct!
Moving piece will print (and potentially a line in that block will error!) when
from.HasPiece is false, even if a piece is present at
Following the same process as before, we simply need to check that
from has a piece and
print("from.HasPiece", from.HasPiece) print("to.HasPiece", to.HasPiece) if from.HasPiece and to.HasPiece and from.Piece.Color ~= to.Piece.Color then -- capture piece elseif from.HasPiece and to.HasPiece and from.Piece.Color == to.Piece.Color then -- invalid move, throw an error return elseif from.HasPiece and not to.HasPiece then print("Moving piece") -- move piece to open square end
Again, this introduces a new bug! If the code after this
if statement relied on the fact that the move being made was, in fact, valid, if
from doesn't have a piece, this will still error!
This can, of course, can be fixed by adding back an
else branch that is identical to the first
elseif, or we can check in the first
from.HasPiece is false:
elseif not from.HasPiece or (from.HasPiece and to.HasPiece and from.Piece.Color == to.Piece.Color) then
Either way works fine, but having them separate allows you to have different "errors" to tell the client without any additional
Now that we know all the logic works, we can reorganize it some using nested
if statements to avoid repeating
to.HasPiece so much!
if from.HasPiece then if to.HasPiece then if from.Piece.Color == to.Piece.Color then -- invalid move, throw an error return else --if from.Piece.Color ~= to.Piece.Color -- capturing piece end else --if not to.HasPiece -- move piece to open square end else --if not from.HasPiece -- invalid move, throw an error return end
While this does take up more lines of code, in my opinion this better because it is easier to understand the "flow" of the code when the logic is written out in a nested fashion like this, as it more closely represents my thought process when I am checking if a move is valid in a real game of chess.
- Add more
print()s than you think you need, to every unique code block.
print()the values of variables used in your controlling logic.
- When you know a section of code is working, remove the
print()s for that section.
- You're done when you have no
print()s left to remove!
About the Author
Valentine Albee - ROBLOX Developer and Professional Programmer
Feel free to shoot me a message on Discord!
Ideas and questions are always welcome. :)