4
\$\begingroup\$

I'm pretty new to programming in general. I work with MySQL at my job but my knowledge of scripting language is rudimentary at best. I've been learning JavaScript via Codecademy but I got bored and set out on my own. I made this Tic Tac Toe game, which is purely text-based and predictably boring. I was just wondering if anyone has suggestions for improving the code, especially in terms of following programming best practices. Also any advice for turning this into a visual game would be greatly appreciated, especially with HTML5.

var board = [0, 0, 0, 0, 0, 0, 0, 0, 0];
var gameOver = true;
var check = true;
var i = Math.floor(Math.random()*2);

var transInput = function(p){
    switch(p) {
        case "A3": return 0;
        case "B3": return 1;
        case "C3": return 2;
        case "A2": return 3;
        case "B2": return 4;
        case "C2": return 5;
        case "A1": return 6;
        case "B1": return 7;
        case "C1": return 8;
        default  : console.log("Entry Unknown!");
    }
};

var transOutput = function(p){
    switch(p) {
        case  1: return "x";
        case 10: return "o";
        default: return " ";
    }
};

var drawBoard = function(){
    console.log("3 " + transOutput(board[0]) + "|" + transOutput(board[1]) + "|" + transOutput(board[2]));
    console.log(" -----");
    console.log("2 " + transOutput(board[3]) + "|" + transOutput(board[4]) + "|" + transOutput(board[5]));
    console.log(" -----");
    console.log("1 " + transOutput(board[6]) + "|" + transOutput(board[7]) + "|" + transOutput(board[8]));
    console.log(" A B C");
};

drawBoard();

if(i === 0){
    console.log("You go first!");
} else {console.log("Computer goes first!");}

while (gameOver){
    if(i === 0){
        var playerMove = transInput(prompt("Your move!"));
        var check = true;
        while(check){
            if(board[playerMove] === 0){
                board[playerMove] = 1;
                drawBoard();
                i = 1;
                check = false;
            } else {
                playerMove = transInput(prompt("Try again."));
            }       
        }
    } else if(i === 1){
        console.log("Computer's turn...");
        var check = true;
        while(check){
            var compMove = Math.floor(Math.random()*9);
            if(board[compMove] === 0){
                board[compMove] = 10;
                drawBoard();
                i = 0;
                check = false;
            }
        }
    }
    var scores = [
        board[0] + board[1] + board[2],
        board[3] + board[4] + board[5],
        board[6] + board[7] + board[8],
        board[0] + board[3] + board[6],
        board[1] + board[4] + board[7],
        board[2] + board[5] + board[8],
        board[0] + board[4] + board[8],
        board[2] + board[4] + board[6]
    ];

    var scoreSum = 0;

    for(var k = 0; k < board.length; k++){
        scoreSum += board[k];
    }

    for(var j = 0; j < scores.length; j++){
        if(scores[j] === 3){
            console.log("You Win!");
            gameOver = false;
            break;
        } else if(scores[j] === 30){
            console.log("You Lose!");
            gameOver = false;
            break;
        }
    }

    if(
        gameOver === true && (
            scoreSum === 54 || 
            scoreSum === 45
        )
    ){
        console.log("Draw!");
        gameOver = false;
    }
}
\$\endgroup\$
5
\$\begingroup\$

Your code's pretty good actually. I don't see any of the typical beginner's syntactical pitfalls. Kudos! (Though I would prefer gameOver, given its name, to be false while the game's running).

Structurally, there are many ways to build it. I'd probably split things into objects or constructors (aka classes).

For instance, I'd make the board an object with its own methods for marking a square, checking for wins, resetting, etc.. Similarly, the players and the turn-taking could be modelled. It may be overkill for a simple Tic-Tac-Toe game, but the resulting code will be simpler to read (I think). It's basically the same strategy as creating views in MySQL to encapsulate complex/reusable queries.

For instance something like this (not a complete implementation)

// Constructor
function Board() {
  this.squares = new Array(9);
}

// Prototype functions (i.e. methods)
Board.prototype = {
  // get number of marked squares
  occupied: function () {
    var i, l, count = 0;
    for( i = 0, l = this.squares.length ; i < l ; i++ ) {
      count += this.squares[i] ? 1 : 0;
    }
    return count;
  },

  // get a row
  row: function (index) {
    var i = index * 3;
    return this.squares.slice(i, 3);
  },

  // get a column
  column: function (index) {
    var i, l, column = [];
    for( i = index, l = this.squares.length ; i < l ; i += 3 ) {
      column.push(this.squares[i]);
    }
    return column;
  },

  // get diagonal squares
  // index 0: from top-left
  // index 1: from top-right
  diagonal: function (index) {
    var step = index ? 2 : 4,
        squares = [];
    index = index ? 2 : 0;
    while( squares.length < 3 ) {
      squares.push(this.squares[index]);
      index += step;
    }
    return squares;
  }
};

Edit: The above code has some issues. One was a straight-up typo on my part (fixed now). The other is a little more subtle: slice() will ignore undefined elements in an array. So (new Array(3)).slice(0)[] instead of [undefined, undefined, undefined]. This could obviously cause trouble, as the array will be shorter than one might assume. So the array should either be seeded with values (zeros for instance), or the slice-call should be replaced with something similar to what column() is doing in the above: Looping and pushing values, even if they're undefined.

Then you can call new Board() to get a, well, a new board. That board, in turn, provides functions for determining the state of play. (Note that most of these functions can be simplified using array functions like forEach and win-conditions can be checked with reduce or a homemade array-sum function. However, that's an exercise for the reader).

With regard to visual output, you could start by simply "printing" to <pre> element on the page instead of the console. A minor step, yes, but just to get the hang of DOM manipulation/interaction. Actually, using your code, I've done that here. Of course, the real next step is using elements for each square, have them react to clicks, etc..

For that, you could consider simply letting the DOM itself handle the state. I.e. rather than a separate array of values, simply use the elements themselves and their attributes to determine if/how a square is marked.

For now, though, I'll stop here. See if you get something useful out of a more object oriented approach, before moving on the HTML GUI. The reason I'm suggesting this, is that the DOM is object oriented already, so getting the game logic on that footing will likely make creating the GUI easier/simpler. At least you're bound to learn something :)

\$\endgroup\$
  • \$\begingroup\$ Thanks for the reply! I'll be going over this all tonight. \$\endgroup\$ – bad_sample Mar 8 '13 at 16:28
  • \$\begingroup\$ @JasonHamje No problem. Don't forget to click the checkmark (but give it a few days to see if you get more/better answers). \$\endgroup\$ – Flambino Mar 8 '13 at 18:07
  • \$\begingroup\$ Thanks again for the help! You've given me a lot to work on. \$\endgroup\$ – bad_sample Mar 10 '13 at 3:17
  • \$\begingroup\$ @JasonHamje No problem, glad I could help \$\endgroup\$ – Flambino Mar 10 '13 at 15:09

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.