DashCode

Modified

Download

TicTacToe DashCode example

Resources

Introduction to DashCode

Overview

DashCode is an alternative to writing native iPhone apps in Objective C. As a combination of JavaScript, CSS, and HTML, DashCode is essentially a client-side Web app that runs on Safari-browsers, OS X desktop and the iPhone.

Note that DashCode does not have access to the underlying hardware beyond that accessible through a Web page.

To provide a flavor and potential of DashCode on the iPhone, the venerable TicTacToe is refactored in DashCode.

The iPhone is the human side of the game playing O, a server plays X.

Starting

DashCode IDE is in the same /Developer/Applications directory as Xcode. The IDE can be learned though experimentation. 

For practice, download the example and add a reset button.

  1. Click on the Library icon, drag a back button onto the canvas.
  2. In the TicTacToe icon, select the back button, and add an onclick event handler.
  3. To reset the game in the event handler:
    1. create a new Model object by: model = new Model();
    2. Reset the button labels. Button7 can be reset to '7' by:

       document.getElementById('button7').object.setText( 7 );

  4. Run.

TicTacToe Server

The TTT server is implemented in Java and should run on most computers. The server is stateless.

Start the server by:

  1. Download the TicTacToe example.
  2. Navigate to TTTServer folder at the command line.
  3. At the command prompt enter:

    java TTTServer

The server receives the state of the game and returns a move between 0-8 or 9 (if there is no move possible).

The server plays X and expects O to move first.

O|O|2
-|-|-
X|4|5
-|-|-
6|7|8

For example, the game state above would be represented and sent to the server running on your computer by:

http://localhost:1490/?OO2X45678

sending to the server:

GET /?OO2X45678 HTTP/1.1

which returns the server move of: 2

The server listens on port 1490, accepting a connection, receiving the HTTP message, determining a move, and sending back to the client.

Note that O and X (capitalized) denote occupied board positions. Also that the server does not cheat.

TicTacToe DashCode Client

DashCode primarily provides the user interface and connects user interface objects to JavaScript code that you write.

The figure at right illustrates the buttons (0-8) in a three column layout.

button0 is highlighted in the upper-left part, the small box indicates a button event (i.e. onClick) is connected to a handler (i.e. clickHandler0) in the lower-right part of the illustration.

Running the code starts the iPhone simulator.

While the concepts are the same as you implemented in Homework 2, some key differences exist and some similarities should be noted.

Similarities:

  1. Model-View-Controller pattern, the View is the UI, the Model handles only game state, the Controller connects View and Model communication.

    Notice that a Model and Controller function serves to create a new model and controller.

    var model = new Model();
    var controller = new Controller();

  2. The same TTTServer is used, called using the XMLHttpRequest object, often used for AJAX-style programming.
    var feedURL = "http://localhost:1490/?"+model.board;	
    var xmlRequest = new XMLHttpRequest();
    xmlRequest.onload = function() { xmlLoaded(xmlRequest); };
    xmlRequest.open("GET", feedURL, true);
    xmlRequest.setRequestHeader("Cache-Control", "no-cache");
    xmlRequest.send(null);
  3. When data arrives from the TTTServer, a callback is made to the following, which passes the data to the Controller.
    function xmlLoaded(xmlRequest) {
    	if (xmlRequest.status == 200) {
                          controller.computer(parseInt(xmlRequest.responseText));
    	}
    }
  4. Can be installed on iPhone. Not speaking from personal experience, but the resource above explains how.

Differences

  1. No memory management, JavaScript has garbage collection.
  2. Dynamic typing in JavaScript.
  3. Can run in Safari browser but that means the server downloading the DashCode file is not the same (port) as the TTTServer. Most browsers, including Safari, prevent access across domains for security reasons; meaning the TTTServer would need to be rewritten to be run by the server that downloaded the DashCode.
  4. So it works in the simulator but possibly not on the iPhone.
var host = "localhost";
var model = new Model();
var controller = new Controller();

function load() {
    dashcode.setupParts();
}

function Model() {
    this.board="012345678";
    this.player='O';
    this.win = function() {
        var wins = "012345678036147258048246";
        for (awin=0; awin<8; awin++) {
            i0 = parseInt(wins.charAt(awin*3));
            i1 = parseInt(wins.charAt(awin*3+1));
            i2 = parseInt(wins.charAt(awin*3+2));			
            if (this.board.charAt(i0) == this.board.charAt(i1) && 
                this.board.charAt(i1) == this.board.charAt(i2) ) 
                return true;
        }
        return false;
    }
    this.move = function(n) {
            if(n > 8 || this.win() || this.board.charAt(n)=='X' || this.board.charAt(n)=='O') return false;
            this.board = this.board.substr(0,n) + this.player + this.board.substr(n+1,9-n);
            if(this.win()) return true;
            if(this.player == 'X')
                this.player='O';
            else
                this.player='X';
            return true;
    }
}

function Controller() {
    this.human = function(n) {
        if(model.win() || !model.move(n)) return;
        var button = 'button'+n;
        document.getElementById(button).object.setText('O');
        if(model.win()) 
            document.getElementById("Player").innerText = "O wins!";
        else 
            document.getElementById("Player").innerText = "X play";
            
        var feedURL = "http://"+host+":1490/?"+model.board;	       // Send TTTServer game state
        var xmlRequest = new XMLHttpRequest();
        xmlRequest.onload = function() { xmlLoaded(xmlRequest); };
        xmlRequest.open("GET", feedURL, true);
        xmlRequest.setRequestHeader("Cache-Control", "no-cache");
        xmlRequest.send(null);
    }
    this.computer = function(n) {
        if(!model.move(n)) return;
        var button = 'button'+n;
        document.getElementById(button).object.setText('X');
        if(model.win()) document.getElementById("Player").innerText = "X wins!";
        else document.getElementById("Player").innerText = "O play";
    }
}

// Callback when an XMLHttpRequest loads a feed; works with the XMLHttpRequest setup snippet
function xmlLoaded(xmlRequest) 
{
	if (xmlRequest.status == 200) {
                     controller.computer(parseInt(xmlRequest.responseText));
	}
	else {
		alert("Error fetching data: HTTP status " + xmlRequest.status);
	}
}
function clickHandler0(event){    controller.human(0);}
function clickHandler1(event){    controller.human(1);}
function clickHandler2(event){    controller.human(2);}
function clickHandler3(event){    controller.human(3);}
function clickHandler4(event){    controller.human(4);}
function clickHandler5(event){    controller.human(5);}
function clickHandler6(event){    controller.human(6);}
function clickHandler7(event){    controller.human(7);}
function clickHandler8(event){    controller.human(8);}