Tic Tac Toe Multiplayer Version Demo Experiment Run Experiment

Tic Tac Toe Multiplayer Version

Jaap Murre

Two-player version of the Tic Tac Toe game. Two players, matched in order of arrival, take turns playing over the Internet. 

This demo script combines the Tic Tac Toe demo with the Multilplayer Game 'chat' demo. The approach taken here can be expanded to develop more complex mulitplayer experiments and economic games.

If you are a registered user and signed in, you can here copy this script and its stimuli to your own account, where you can edit it and change it in any way you want.

It is absolutely free to register (no credit card info asked!). You can then instantly copy this experiment with one click and edit it, change its accompanying texts, its landing page, stimuli, etc. Invite your colleagues, friends, or students to check out your experiment. There is no limit on how many people can do your experiment, even with a free account.

The catch? There is no catch! Just keep in mind that with a free account, you cannot collect data. For teaching that is usually not a problem. For research, prepaid data collection (unlimited subjects) starts as low as €10.00 for a 10-day period.

//////////////////////////////////// Game Class ////////////////////////////////

function Game() {
    this.receive_poll_interval = 1000; // ms
    this.opponent_poll_interval = 1000; // ms
}

Game.prototype.wait_for_opponent = function() {
    var opponent_waiting = retrieve('opponent_waiting','script');
    if (!opponent_waiting) {
        this.first = 1; // I was here first
        store_now(true,'opponent_waiting','script');
        this.game_id = 1*(new Date()); // 1* converts to number
        store_now(this.game_id,'game_id','script'); 
        while (true) {
            if(!retrieve('opponent_waiting','script')) {
                this.game_id = retrieve('game_id','script');
                break;
            } else {
                await(this.opponent_poll_interval);
            }
        }
    } else {
        this.game_id = retrieve('game_id','script');
        store_now(false,'opponent_waiting','script'); // Affirm by clearing
        this.first = false; // I was second; someone was already waiting
    }
}

// Sends content over (optional) named channel and waits until it has been received 
Game.prototype.send = function(content,channel) {
    content = {
        data: content
    }
    content = JSON.stringify(content); // Ensure correct JSON-ification on server
    channel = channel || "channel";
    var id = channel + '_' + this.game_id,
        signal;
    store_now(content,id,'script');
    while (true) {
        signal = retrieve(id,'script');
        if(signal) { // Wait until the signal has cleared, i.e., has been received
            await(this.receive_poll_interval);
        } else {
            break;
        }
    }
}

// Receive content over (optionally) named channel. Waits until reception.
Game.prototype.receive = function(channel) {
    channel = channel || "channel";
    var id = channel + '_' + this.game_id,
        signal;
    while (true) {
        signal = retrieve(id,'script');
        if(signal) {
            store_now(false,id,'script'); // clear channel; signals reception
            return signal.data;
        } else {
            await(this.receive_poll_interval); // wait and try again
        }
    }
}

////////////////////////////////// End Game Class ///////////////////////////////


text("Waiting for opponent");
var game = new Game();
game.wait_for_opponent();
clear();

/*
var first_turn = true;
while (true) { // Simple turn-taking chat
    if (game.first && first_turn) { // first_turn helps determine who sends first
        first_turn = false;
        input("Send message","message");
        game.send(response.message);
        text("Message received");
    } else {
        text("Waiting for message")
        var msg = game.receive();
        text(msg);
        await(5000);
        input("Send message","message");
        game.send(response.message);
        text("Message received");
    }
}
*/

var r, c, blocks = [], b, selected_block,
    header = addblock(21,1,60,16);

for (r = 0; r < 3; ++r) {
    for (c = 0; c < 3; ++c) {
        b = addblock(21+r*20,21+c*20,18,18,"lightgrey");
        b.on("click",onClick(b));
        b.index = r*3 + c;
        blocks.push(b);
    }
}

function onClick(block) {
    return function() {
        if (block.used || !your_turn) { // Cannot change X or O, if already one present
            return; // Also, you can only make a move if it is your turn
        }
        your_turn = false;
        var b = block, // This ties the block to this function
            event;
        b.style("background-color","yellow"); // Show as selected
        event = awaitkey("o,x"); // Wait for either the O or X key to be pressed
        b.text(event.key,200);   // Show the key's letter at size 200%
        b.style("background-color","lightgrey"); // Show as not selected
        b.used = true;  // This block (position) has been taken
        b.letter = event.key;
        game.send({index:b.index,letter:b.letter});
        header.text("Waiting for response");
        if (checkWinner()) {
            header.text("You won!");
        }
        // After having sent a letter, wait for the response
        var r = game.receive(); // Here comes the letter
        blocks[r.index].letter = r.letter;
        blocks[r.index].text(r.letter,200);
        if (checkWinner()) { // Did my opponent win?
            header.text("You lost!");
        } else {
            header.text("Your turn"); // If not, I can try and click again
            your_turn = true;
        }
    }
}

function checkWinner() {
    var b = blocks;
    
    function check(x,y,z) { 
        if ((b[x].letter === b[y].letter && b[y].letter === b[z].letter) 
                && b[x].letter) {  // Include last to check for empty string
            return [x,y,z];                
        }
    }
    
    var winner = check(0,1,2) || check(3,4,5) || check (6,7,8)
            || check(0,3,6) || check(1,4,7) || check(2,5,8)
            || check(0,4,8) || check(2,4,6);
    if (winner) {
        await(500);
        blocks[winner[0]].style("background-color","orange");
        blocks[winner[1]].style("background-color","orange");
        blocks[winner[2]].style("background-color","orange");
        return winner;
    }
}

var first_turn = true, 
    your_turn = false;
    
if (game.first && first_turn) { // first_turn helps determine who sends first
    first_turn = false;
    header.text("Your can start");
    your_turn = true;
} else {
    var r = game.receive(); // Here comes the letter
    blocks[r.index].letter = r.letter;
    blocks[r.index].text(r.letter,200);
    header.text("Your turn"); 
    your_turn = true;
}

awaitkey("ESCAPE"); // At any point, you can press Escape and quit
// Normally we would send a message to the opponent

You can download the files as follows: Click on the file (link) and then right-click and choose Save as... from the menu. Some media files (e.g., sound) will have a download button for this purpose.

This experiment has no files of this type
This experiment has no files of this type
This experiment has no files of this type
This experiment has no YouTube links
This experiment has no files of this type