Yet Another TicTacToe

Started by
4 comments, last by Zahlman 18 years, 11 months ago
Yet another fledgling game programmer brings his tic tac toe game to the scrutiny of his superiors... or take a look at this plz

#include <iostream>
using namespace std;

char board[10] = {'0','1','2','3','4','5','6','7','8','9'};

// Returns either X or O as a char if one is the winner... else (char)0
char checkWin()
{
   if(board[1] == board[2] && board[1] == board[3])
      return board[1];
   if(board[4] == board[5] && board[4] == board[6])
      return board[4];
   if(board[7] == board[8] && board[7] == board[9])
      return board[7];
   if(board[1] == board[4] && board[1] == board[7])
      return board[1];
   if(board[2] == board[5] && board[2] == board[8])
      return board[2];
   if(board[3] == board[6] && board[3] == board[9])
      return board[3];
   if(board[1] == board[5] && board[1] == board[9])
      return board[1];
   if(board[3] == board[5] && board[3] == board[7])
      return board[3];
   return (char)0;
}// End checkWin

void clearBoard()
{
   board[1] = '1';
   board[2] = '2';
   board[3] = '3';
   board[4] = '4';
   board[5] = '5';
   board[6] = '6';
   board[7] = '7';
   board[8] = '8';
   board[9] = '9';
}// End clearBoard

void displayBoard()
{
   cout << "_\b" << board[1] << "|_\b" << board[2] << "|_\b" << board[3] << endl;
   cout << "_\b" << board[4] << "|_\b" << board[5] << "|_\b" << board[6] << endl;
   cout << board[7] << "|" << board[8] << "|" << board[9] << endl;
}// End displayBoard

int getInput()
{
   int move;
   while(true)
   {
      cout << "Please enter a move: ";
      cin >> move;
      if(board[move] != 'X' && board[move] != 'O')
         return move;
      else
         cout << "That move has already been made." << endl;
   }// End while
}// End getInput()
int main()
{
   int choice;
   int winner;
   int turn = 0;
   // Initialize any remaining variables
   while(turn < 9)
   {
      ++turn;
      displayBoard();
      choice = getInput();
      switch(turn % 2)
      {
         case 1:
            board[choice] = 'X';
            break;
         case 0:
            board[choice] = 'O';
            break;
      }// End switch
      if(winner = checkWin())
         break;
   }// End while
   displayBoard();
   switch(winner)
   {
      case 'O':
         cout << "O has won the game." << endl;
         break;
      case 'X':
         cout << "X has won the game." << endl;
         break;
      default:
         cout << "The game was a CAT." << endl;
         break;
   }// End switch
   return 0;
}// End main

I went with a "1 based array since it makes it where I don't have to use another operation like choice - 1 at the cost of a few bytes. But then again I am sure there are several optimizations to be made. And out of curiosity is there any way to display text as underlined in the console... I know you can with an API. Oh well... any critique would be wonderful.
Advertisement
That looks pretty good to me.

You've got a function to check if the square is already taken, another one to check if there is a complete row (testing every combination, seems a fairly good way of doing it), and a function to display the board.

If you find it easier using an array, that's good, but you could use a vector, which is dynamically resizable (not that that helps you here, but it's an alternative).

Well done!
The only problem with me and vectors is that my CS department in school hates them. I'm trying to learn them on the side as well as the rest of the standard c++ library. Any suggestions on good articles/books/tutorials for that... besides the ones here?
I'll let you get away with the 1-based array, simply because you aren't trying to do any index calculations (convert row,column pair to an index) and because I'm tired of arguing about it. :) See other comments in snippets below:

#include <iostream>using namespace std;char board[10] = {'0','1','2','3','4','5','6','7','8','9'};// Returns either X or O as a char if one is the winner... else (char)0char checkWin(){  // Looping over an array of "winning combinations" works rather more nicely  // (to avoid repetition). Also, you can get away with returning a boolean,  // with careful design - because the game can only be won by the player who  // just moved.}// As a general comment, if you really *need* "end whatever" comments on your// braces, it's because your functions etc. are too long. ;)void clearBoard(){  // FFS, use a for loop here:  for (int i = 1; i < 10; i++) { board = '0' + i; }}void displayBoard(){   cout << "_\b" << board[1] << "|_\b" << board[2] << "|_\b" << board[3] << endl;   // What are you expecting the \b stuff to do - underline the numbers? It   // might work if you could go back to the 70s and get hold of a teletype...   // you'll probably instead want to draw -'s on a separate line :/}int getInput(){   int move;   while(true)   {      cout << "Please enter a move: ";      cin >> move;      if(board[move] != 'X' && board[move] != 'O')         return move;      else         cout << "That move has already been made." << endl;   }  // You might want to do some checking for the input being a number,  // and for being between 1 and 9? :)}      switch(turn % 2)      {         case 1:            board[choice] = 'X';            break;         case 0:            board[choice] = 'O';            break;      }// End switch// ew ew ew ew ew. Instead:char symbols[] = { 'O', 'X' };board[choice] = symbols[turn % 2];
Actually I agree with you on the comments after braces. However, one of my professors takes away credit if they aren't there... so just kind of in the habit of it. I understand that since this is console based... speed is not really an issue. But wouldn't unrolling several of the loops like i did create faster code. Instead of how you have in your markup, where each check requires a loop condition check as well as an increment... two extra operations... I know, I know it doesn't seem like much... I use 2 more operations for a slightly longer block of code. Anyways, I understand not wanting to handcode stuff when you don't have to. Not only is it less error-prone, but is quicker to type and sometimes quicker to understand. And I do agree that I could just return a boolean for checkWin. However, I'm going ahead and assigning the value i return to the person that wins and exiting. It doesn't make faster code to just use a boolean... At least I don't think so. Then again if I wanted fast code, I'd be doing this in assembly not c++. Wow I think I'm ranting more about myself right now. I do like the edit at the end on how I change the status of the board. Didn't think of that. Also out of curiosity how would I go about error checking to see if cin gets an int instead of a float or string. Checking if it's in range is easy and will soon be implemented. Next game up for me is connect4 on a 5x8 board. Then I guess I'll start getting into 2D. Thanks for the comments all. Even if I didn't like them it's nice to see other ways to do things.
Quote:Original post by NightStalker
Actually I agree with you on the comments after braces. However, one of my professors takes away credit if they aren't there... so just kind of in the habit of it.


Oh, you poor thing :(

Quote:I understand that since this is console based... speed is not really an issue. But wouldn't unrolling several of the loops like i did create faster code. Instead of how you have in your markup, where each check requires a loop condition check as well as an increment... two extra operations... I know, I know it doesn't seem like much... I use 2 more operations for a slightly longer block of code.


Maybe. Processor design is complicated these days, and a larger chunk of code might not fit in the "instruction cache", such that it gets slowed down again. Anyway, compilers have been able to do manual unrolling of loops for years, and generally speaking are Better Qualified Than You(TM) to judge how far (if at all) to unroll. (If you were better qualified, you would have a job working for a company that makes compilers ;) ) Anyway, prefer code that is clear and simple. Repetition doesn't make things clearer, because you end up having to process each repetition and mentally deduce what the pattern is and make sure there aren't any subtle deviations. Repeating things also increases the chance of a copy-paste error :)

Quote:Anyways, I understand not wanting to handcode stuff when you don't have to. Not only is it less error-prone, but is quicker to type and sometimes quicker to understand. And I do agree that I could just return a boolean for checkWin. However, I'm going ahead and assigning the value i return to the person that wins and exiting. It doesn't make faster code to just use a boolean... At least I don't think so. Then again if I wanted fast code, I'd be doing this in assembly not c++.


Heh. It's not a performance issue; it's a cleanliness issue - simplifying the *overall* logic. Getting a really good feel for that will take years of experience though. And I don't claim to be an expert (although I do think I'm pretty good). ;)

Quote:Wow I think I'm ranting more about myself right now. I do like the edit at the end on how I change the status of the board. Didn't think of that. Also out of curiosity how would I go about error checking to see if cin gets an int instead of a float or string.


I thought you'd never ask. :D (Just a reminder, if you care to check out the rest of that page, to please, please use std::string for textual data, although they also show you how to work with character buffers...)

Quote:Checking if it's in range is easy and will soon be implemented. Next game up for me is connect4 on a 5x8 board. Then I guess I'll start getting into 2D. Thanks for the comments all. Even if I didn't like them it's nice to see other ways to do things.


Connect4 is traditionally on a 7(across) by 6(down) board :)

Anyway, good luck with it ^^b

This topic is closed to new replies.

Advertisement