Awhile back, I gave myself a goal of trying to create a simple Tic-Tac-Toe game within a week. I didn’t want to just copy the ubiquitous code out of a book, though. I wanted to take a project from intial design to implementation on my own. This project was not meant to be something commercial quality, nor is it meant for public consumption. It was just a way for me to test my rusty programming skills as well as practice using some of the tools I was experimenting with before, such as Subversion.
I did it. I called it GBTTT_CLI, which stands for GBGames Tic-Tac-Toe Command Line Interface. No graphics. No sounds. Just a simple Tic-Tac-Toe game. You decide who goes first, and each player will select one of the spots to place his/her piece, either an X or an O. At the end, the game will tell you who has won. Simple. B-)
What Went Right:
- It was not made with just one code file.
Tic-Tac-Toe is a very simple game. It doesn’t need to be object-oriented, nor does it need much in the way of content. Still, I wanted to separate elements, such as Players and the Board. For the most part I succeeded. I could replace the Player with an AIPlayer at a later time easily. I could probably change the implementation of the TicTacToe game class so it can be done over a network without changing much of the other code, if any.
- Development went along fairly quickly.
GBTTT_CLI was the first project I have worked on while using a safety net. Revision control tools are amazing and I wish I would have known about them years ago. I used Subversion, and while I rarely needed to backtrack, especially in a small project like this one, it was useful to go through my log to see what changes I made before. There was also a time when I made a LOT of changes only to get nowhere with them. It was easy to revert all the code back to the way it was before the changes were made. It saved a LOT of time for me when I could have otherwise just been trying to make changes to get back to a stable state. With a single command, I was there.
Another thing that helped was the ease I had with actually coding. I underestimated my coding abilities because I thought I was starting from scratch with C++. The truth was, I could program, and I just needed to learn C++’s syntax correctly. There were a few things that I got tripped up on, such as NULL instead of null as in Java, but a quick look at my reference material showed me where I was going wrong.
- The game got completed.
It was a project that I was able to finish. It may not be much, but it is more than what I had before, and I am proud of that fact. It’s also shows that I have the ability to work on my own designs.
What Went Wrong:
- It took me too long to get here.
My goal was to get this program done in a week, and in total programming time, I did way better than that. Unfortunately if you count the days from when I first posed this goal to myself until I finished it, it was way longer. When I started coding, I got a lot done within a few hours. Then nothing for days until I tried to work with it again. Then I got some strange error. Nothing for weeks. Then finally I finished it. So I had a few hour spurts of productivity followed by long periods of nothing. To be fair, it was not like I was just avoiding the project. I had other responsibilities in my life, such as homework. But to be brutally honest, I wasn’t making the time for this project either.
- I overdesigned the classes.
It is Tic-Tac-Toe. How do you overdesign it? I did. I thought that if I created the classes first, they would be useful as tools to build the actual game. I still think that it would be good to design the project this way, but my problem was in designing in specific features. The Player class has a Record member. The Record class holds the Win/Lose/Tie records of that Player. I wrote the classes in this way because I originally envisioned a single game session letting you play multiple rounds. In the end, I decided not to implement that since it did not need to be done to accomplish my goals. So now a Round is useless in the project. I didn’t spend too much time making it, but there are other elements that were made but not used. For instance, I also thought this would be a great way to learn network programming, so I tried to anticipate it by making Boards unique. I could have saved time and effort by deciding to add such things later or in a new project.
- The code is ugly.
When I originally programmed in QBasic, I had no concept of code structure and variable naming. When I learned C++, I thought it made sense to follow what C++ did: variable and function names would look_like_this. Then I learned Java. Java programming practices are clean looking. A class name looks like: ClassName. A variable looks like: variableName. When I went back to C++, I got it in my head that I probably should keep it looking like C++, where standard library naming conventions have the underscores as mentioned above. That is when I started the project.
Then I found a C++ coding convention guideline. Apparently Java-like style has its place in C++, too. Well, I did not want to go back and rename everything, so when I continued to work on the game, I kept using the same style rather than confuse everything, me included. The end result is commented, cleanly separated, but somewhat hard to read. A sample:
// switch to next player
game.next_player();
// keep asking for input until value is legal
int move = player_current->get_move(board);
bool is_legal_move;
is_legal_move = board.isLegal(move);
Huh. Well as you can see, I did not succeed in keeping the style uniform. Board::isLegal() should be changed to Board::is_legal() to accomplish that. Still, it’s hard to read because of the style.
I know there is normally 5 of each in a postmortem, but come on, it’s Tic-Tac-Toe!
What I Learned
- Practicing programming is important. I should treat it as such and make the time for it rather than hoping I have an opening in my schedule. There won’t be one if I leave it up to everything else.
- Iterative programming is useful because I can avoid overdesigning something that won’t get used
- Programming is still fun, just like I remember it.
Even though I am disappointed in how some things turned out, I am satisfied. It’s a completed project! I was originally going to leave it in a state of incompleteness and start working on a more attractive project idea, but I decided to be “great” rather than “good enough”. If I keep that attitude with these smaller projects, it will be a good habit to have when I tackle larger ones.
I think I will post the code at a later time. I originally did not think it would be something I would publish, but I figure it has to help someone besides me. I’ll need to add a proper README and installation/compilation instructions first.
EDIT: Hey, I delivered. B-) And in two forms:
gbttt.tar.gz
gbttt.zip
2 replies on “Postmortem: GBTTT-CLI”
I took a quick look at your code and I noticed these lines:
// get a fresh copy of the board
Board board = game.get_board();
if (&board == NULL) {
// GBGames: I can’t get the stream brackets to work in this blog
}
The check is completely unnecessary. The address of board can’t be NULL. Judging from your code and what you wrote above, I suspect you confuse Java’s null and C++’s NULL. null denotes a null-reference where NULL denotes a null-pointer. C++ does not have null-references; a reference can never be null. Also, NULL is exactly the same thing as 0, so you can replace every instance of NULL with 0 (and vice versa, but that would be weird).
Just thought it’s worth pointing out.
You’re right. I was treating it like Java’s null. I was checking to see if the address was NULL, but it wouldn’t be NULL unless I set it myself. So I am still learning, obviously, but it’s progress, and getting comments from others certainly helps a lot in that progress.
Also, I found that the comment didn’t get output correctly. I fixed it with the note as you can see. The blog thinks that the brackets are meant to be HTML/XML and so cuts off the rest of the comment. I had the same problem when listing my code above. I ended up getting rid of the cout lines since they weren’t necessary to make my point.