Wednesday, March 1, 2017

The Tic-Tac-Toe Story



The game is finally written, all functions are implemented, the code is live and I promised you a (hopefully) interesting story. Here goes nothing! (Picture Copyright: My own work. Use it as you please.)

 

The Beginning


When I started out working on this game I was all excited as you can probably imagine. This is the very reason I learn the language for after all. In the beginning I saw little trouble and thought that it could be finished in less than a day or two. All went well, and the main functions were written in less than two hours. The replacement of tiles worked, the function to check the validity of the moves, naming players, and all the rest of what was needed to test the functionality.

Little did I know at that stage how much trouble was lurking in the shadows just waiting to get me at first chance! The first version of code was planned out like this:

Having the board array as is described in the header of the program. This was exactly the part where it all started to go badly wrong. I wanted to have the array not above main, not in main, but in its own function only to be called once from the menu function. To have an array in a function and to make sure it keeps its data I did the one thing I learned previously. Set the array to static. 

NEVER! I warn you, never put an array in a function in C++ and think you would get away with it. This introduces an error that is not displayed if you don't run the program thru the debugger and set the break-point in a different function where the array is stored in. You would most likely get this error:

+ gameBoard Jump.exe!0x0130c000 (Type information missing from symbol file) {46 '.', 46 '.', 46 '.'} char[3] *

I tried finding information about that sort of error, but only got 5 results in return. That the culprit was using the static keyword with an array placed in a function, who would have expected that? Had it not been for Enoizat a member in a coding community I turned towards for help with this problem that was way over my head (credit where credit's due!) I would probably still be searching for ways to fix my original code. Instead I simply wiped the canvas and started from the beginning. As I have done lately on almost every project past 7.15. The only sensible thing to do in such a situation.

 

Some Successes More Mistakes


My spirits were still high, although from discovery to deciding to change my original game plan, which at first seemed solid enough to be warranted for keeping it the way it was, I gave i another shot. Everything was clear to me, as the foundation that enables one to write such games as tic-tic-toe has already been laid by finishing Programming Challenge 7.8 [Lo Shu Magic Square] The only difference between this and the game is only the checking for winning moves and/or draw. Another part that is slightly different is introducing a game loop that allows for continuous play. In the beginning I had a simple check in place that basically looked like this:

int q = 9; 

do
{
   .... 
} while (q < 9);

This idea, in later stages, eventually led to the idea of using a function that would keep track of moves and was designed to be used in several different functions that are having to do with placing tiles, assigning the name to players, showing the moves and so on. That, though, at this stage was of little concern to me. I simply wanted a short program that does everything with as little amount of functions, function calls as possible. 

My first success after many failures was that I got to the stage where everything seemed to look good and did work. Again, this was my impression, that this time I got it right. Instead I introduced another error which was harder to fix than simply placing an array as global. Since it was going to change anyway, I threw the concern overboard and placed it there ... Just simply to spare me the need of that many parameters in function calls for one lousy 2D-array that has the only purpose of displaying a vanilla game board. That is about the only thing I really kept during all revisions which number is more than a dozen. 

Blinded by success I thought that now I solved it. Put some comments here and there, describe your programs functions, and you are set to publish the thing. Yet, again, I managed to introduce an error that would soon enough become apparent to me. When I decided to use tick as a counter, and gave it its own functions, I ran into almost the same problem as I had with my initial array setup. This time around there was no error message that would warn me of what is to come. Would be too easy that way, ne? I had the function in place, had a variable I called tick in there, used the static keyword, and let it count to 9. My intention there was to say: 

If the counter reaches 9, then either of two conditions have been met. A draw, which, in that stage of writing my code should return a draw from the winning function, or it is a win and the program should then return to main menu and exit the game loop. This was a great idea, and had a positive side-effect as well. As you can see when reading my tic-tac-toe game code, you will that numerous functions have a ternary in place that in one way or other depend on tick to change the conditions.

tick % 2 == 1 ? highScore[0] = scoreP1 : highScore[1] = scoreP2;

This is one of them. Now imagine if a game was won, and the players would switch automatically based on the above tick variable, static, residing in its own function, after a game has been won by either player and another game is started? This is exactly what has happened. I was in awe that this design choice would do it for me - and it came quite unexpectedly. A little thing, holding a whole world of trouble and headaches in store. As Alice Cooper sang - It's the little things ... As an aside, it is also the things that look to good to be true that aren't ... As was in this case, sadly so!



Why? Simply because my whole program depended on the function and functionality of it. It was called from the menu function numerous times as an exit condition of the game loop. And in several other places where the consequences of that malfunction would soon enough become clear. And the design choice that I made, and how I tried to implement it, was all wrong. If I'd given it some more days, I would probably have figured out a way to make use of it correctly. But this was again a fork in my road to finishing the program that shut me down and set me back to square one. 

Imagine a game is over, based on the tick in the function. The tick would continue to count, so if a draw is reached, that draw condition would exit the program the first time and allows for another round to be started. The second time, well, it would not work. So, thought I, why not introduce a reset to the whole thing? Bad choice, once again. The counter was reset after reaching 9, so far so good. Or rather bad. Because what happens when a game is played and the game exits simply because counter reached nine, yet only 3 moves had been made?

That was about the time I found out why this would never work. At least not in the current condition the code was in. So I tried badly to fix this, by changing tick from static to simply int and return the tick after every call. Or trying to alter the exit conditions in the menu, so that, after counter has reached nine, and is reset to 0, the 0 condition should be used to determine a draw if no winning condition could be found, and go back to the menu.

This was not the only thing that went wrong. Once a round is won, another round was started, the players have been switched, so 1 becomes 2, and 2 becomes 1, it would work to assign the correct tiles. But only about once or twice, after a third round, Player 2 would suddenly be called twice and 2 of her pieces are placed on the board. Or even more often. This was something I simply couldn't fix around somehow. Bad design choice, bad way of carrying out the writing of code. 

 

Finally

 

Hours of hard work down the drain once more. Ten days passed, by which time I published a little update, after which I confidently started once more as I was at the point of seemingly having fixed my bad design choices, and the game would run. And i was also confident that I would make it as promised the same day or the day after to upload the game for you to enjoy. Now, again, I managed to introduce small errors, such as the check for winning conditions.

On the first look, what would be so hard with that? As mentioned this problem was solved in the Lo Shu Magic challenge. But simply calling it didn't work. Well, no, that isn't correct. It did work for some conditions, not for others. Once I had written all conditions for every possible winning positions, the following thing happened. When the function was called after the first move, it would declare a win ... I was ready to cry ... Seriously! What was going on, why does it do this to me ... 

That is when I made a discovery, as I needed some distraction, else I would really have started to cry. There is a book about Tic-Tac-Toe, by a professor József Beck, who is professor at Rutgers University, called Combinatorial Games Tic-Tac-Toe Theory It is all about theory, and a huge amount of mathematics. Nonetheless very interesting for the parts I read and could understand. If for no value in advancing the project, which was almost finished yesterday, and finally finished today, it wasn't the baddest way of distracting myself from a problem I have been working so hard on. And every time I was so close to finish it off, give it some polishing touches, I was set back ...

Every bad thing comes with a good thing, or was it the other way around? Without all the errors, mistakes, bad design choices which, probably, a seasoned programmer would find numerous in the published code, the program would not be in its current form. Meaning for instance the loading of a file to read the instructions from. Or the implementation of a primitive high score function, to name another part that is not original to the challenge but an addition I found would be great to have. If though, without output to file is somewhat pointless past shutting the game down. Also I tried myself with typedef, as I found this mentioned in my book, under C++ keywords, so I thought why not? Might be just what I was looking for, and finally I decided to learn and integrate it into my vocabulary. 

Am I satisfied with the way the game as such is? Yes! And even though forbidden somewhat proud of myself as this is one of the biggest projects in my 6 months (almost), since I started learning the language. It also gave me an idea what probably will probably write besides all the challenges: Reversi and similar games. I think that this is feasible by now. Adventures, text or other, I guess that is above and beyond my level, and something for the future. Maybe once I learned about classes and such that this comes into reach. One step after the other though!

After all this text, which probably turned out not to be that interesting after all, there is yet two final challenges in this chapter for me to be written to call this one done. One of which has to do with 2D-Arrays doing things, which, after keeping myself busy for so long with writing Tic-Tac-Toe, I hope to be able to wrap my head around this one. And, lastly, a challenge involving multidimensional arrays - which is a group project. Yes, I will try myself on that one as well. No group, no problem? I simply make a call to my five other personalities they will help out. j/k 

Now it is once more time to go back to my IDE, wish my fellow learners, that they be able to glean some useful information from this long wall of text that will help them not to stumble over all the little things that can make and break their projects. Also I wish all of my visitors a good day, or night, whatever it is. Have a pleasant one! 

No comments:

Post a Comment