What you really want is to eliminate ad nauseum references to constants, whether they be named or bare:
for_each_chess_square (row, col) {
/*...*/
}
If you're actually going to proliferate the constant by repeating such loops and whatnot, it's best to stick with 8
.
8
is self-describing; it's not a macro that stands for something else.
You Ain't Never Gonna (TM) turn it into a 9x9 chess program and if you ever do, the proliferation of 8 will not be the major difficulty.
We can search a 150,000 line code base for the token 8, and classify which occurrences mean what in seconds.
Far more important is to modularize the code so that the chess knowledge is concentrated in as few places as possible. It's better to have one, two, maybe three chess-specific modules in which a literal 8 occurs, than thirty-seven modules laced with chess-specific responsibility, referring to 8 through a symbolic name.
When or if this 8 constant becomes a source of tension in your program, you can easily fix it at that time. Fix real problems that are happening now. If you don't feel that you're hampered by that particular 8, go with that instinct.
Suppose that in the future you want to support alternative board dimensions. In that case, those loops will have to change whether they use a named constant or 8
, because the dimensions will be retrieved by some expression like board.width
and board.height
. If you have BOARD_SIZE
instead of 8
, these places will be easier to find. So that is less effort. However, you must not forget about the effort of replacing 8
with BOARD_SIZE
in the first place. The overall effort is not lower. Making one pass over the code to change 8
to BOARD_SIZE
, and then another to support alternative dimensions, is not cheaper than just going from 8
to alternative dimension support.
We can also look at this from a purely cold, objective risk/benefit analysis. The program has bare constants in it now. If these are replaced by a constant, there is no benefit; the program is identical. With any change, there is a nonzero risk. In this case, it is small. Still, no risk should be taken without a benefit. To "sell" the change in the face of this reasoning, we have to hypothesize a benefit: a future benefit which will help with a different program: a future version of the program that doesn't exist now. If such a program is being planned, this hypothesis and its associated reasoning are bona fide and should be taken seriously.
For instance, if you're days away from adding more code that will further proliferate these constants, you might want to do away with them. If those instances of the constants are approximately all the instances that will ever exist then why bother.
If you ever work on commercial software, ROI arguments will also apply. If a program isn't selling, and changing some hard-coded numbers to constants won't improve the sales, you will not be compensated for the effort. The change has zero return on the investment of time. ROI arguments generalize beyond money. You wrote a program, investing time and effort, and got something out of it: that's your return, your "R". If by making that change alone, you get more of that "R", whatever it is, then by all means. If you have some plan for further development, and that change improves your "R", ditto. If the change has no immediate or forseeable "R" for you, forget it.
i
andj
for the loop variables. I cannot for the life of me figure out which one is supposed to represent rank and which one is supposed to represent file. Ranks range from 1..8 and files range from a..h, but in your case, bothi
andj
range from 0..7, so that doesn't help me see which is which. Is there some international letter shortage crisis I don't know about, or what is wrong with renaming them torank
andfile
?foreach(var rank in Ranks)
. I'd also consider merging both loops into one where each element is a (rank, file) tuple.