<!DOCTYPE html>

<html>

<head>
    <meta charset="utf-8" />
    <title>TicTacToe</title>
</head>

<body>
    <canvas id="canvas" width="400" height="400"></canvas>

    <script>
        //All helper functions
        isBetween = (num, a, b) => {
            return num >= a && num <= b;
        }

        randInt = (low, high) => {
            return Math.floor(Math.random() * (high - low + 1)) + low;
        }

        choice = arr => {
            return arr[randInt(0, arr.length - 1)];
        }

        //Checks if every value in an array equals an item
        equals = (arr, item) => {
            return arr.filter(a => {
                return a === item;
            }).length === arr.length;
        }

        //Returns number of items in array that equal an item
        equallen = (arr, item) => {
            return arr.filter(a => {
                return a === item;
            }).length
        }

        //Checks if any value in the array equals an item
        equalanyof = (arr, item) => {
            return equallen(arr, item) > 0;
        }

        //Should be scalable, but it uses default elements for calculations and tracking
        let canvas = document.getElementById("canvas");
        let ctx = canvas.getContext("2d");
        const width = canvas.width;
        const blockSize = canvas.width / 3;
        const lineSize = blockSize / 5;

        //Draws background
        ctx.fillStyle = "rgb(225, 225, 225)";
        ctx.fillRect(0, 0, 400, 400);

        //Title page
        ctx.fillStyle = "rgb(0, 0, 0)";
        ctx.font = width / (250 / 17) + "px Arial"; //34
        ctx.textAlign = "center";
        ctx.fillText("Tic Tac Toe", width / 2, width / (2 + 2 / 3)); //200, 150

        //Button for starting
        ctx.fillStyle = "rgb(200, 200, 200)";
        ctx.fillRect(width / 3.2, width / 2, width / (2 + 2 / 3), width / 8); //125, 200, 150, 50
        ctx.fillStyle = "rgb(0, 0, 0)";
        ctx.font = width / (200 / 9) + "px Arial"; //18
        ctx.fillText("Start", width / 2, width / (40 / 23)); //200, 230

        //Uses an array so a forEach loop can scan it for the correct tile
        let tileArray = []; //Contains all tiles
        let available = []; //Contains only available tiles

        class Tile {
            constructor(x, y) {
                this.x = x * blockSize;
                this.y = y * blockSize;
                this.state = "none";
                tileArray.push(this);
                available.push(this);
            }

            draw() {
                ctx.strokeStyle = "rgb(175, 175, 175)";
                ctx.lineWidth = blockSize / 10;

                if (this.state === "X") {
                    ctx.beginPath();
                    ctx.moveTo(this.x + blockSize / 4, this.y + blockSize / 4);
                    ctx.lineTo(this.x + blockSize / (4 / 3), this.y + blockSize / (4 / 3));
                    ctx.moveTo(this.x + blockSize / 4, this.y + blockSize / (4 / 3));
                    ctx.lineTo(this.x + blockSize / (4 / 3), this.y + blockSize / 4);
                    ctx.stroke();
                } else if (this.state === "O") {
                    ctx.beginPath();
                    ctx.arc(this.x + blockSize / 2, this.y + blockSize / 2, blockSize / 4, 0, 2 * Math.PI);
                    ctx.stroke();
                }

                //Removes this from the available array
                const ind = available.indexOf(this);
                available = available.slice(0, ind).concat(available.slice(ind + 1, available.length));
            }
        }

        //Defines the game
        let game = {
            state: "start",
            turn: "Player",
            player: "X",
            opp: "O"
        }

        //Generates tiles
        for (let x = 0; x < 3; x++) {
            for (let y = 0; y < 3; y++) {
                new Tile(x, y);
            }
        }

        //Gets the mouse position
        getMousePos = evt => {
            let rect = canvas.getBoundingClientRect();
            return {
                x: evt.clientX - rect.left,
                y: evt.clientY - rect.top
            }
        }

        //Checks for win conditions
        checkCondition = () => {
            //Local variables are created to make access easier
            let as = tileArray[0].state;
            let bs = tileArray[1].state;
            let cs = tileArray[2].state;
            let ds = tileArray[3].state;
            let es = tileArray[4].state;
            let fs = tileArray[5].state;
            let gs = tileArray[6].state;
            let hs = tileArray[7].state;
            let is = tileArray[8].state;

            //Equals function checks if each value in the array has a state of X or O
            if (equals([as, bs, cs], "X") || equals([ds, es, fs], "X") || equals([gs, hs, is], "X") ||
                equals([as, ds, gs], "X") || equals([bs, es, hs], "X") || equals([cs, fs, is], "X") ||
                equals([as, es, is], "X") || equals([cs, es, gs], "X")) {
                alert("Player wins!");
                game.state = "over";
            } else if (equals([as, bs, cs], "O") || equals([ds, es, fs], "O") || equals([gs, hs, is], "O") ||
                equals([as, ds, gs], "O") || equals([bs, es, hs], "O") || equals([cs, fs, is], "O") ||
                equals([as, es, is], "O") || equals([cs, es, gs], "O")) {
                alert("Opponent wins!");
                game.state = "over";
                //It is a tie if none of the above conditions are fulfilled and there are no available tiles
            } else if (available.length === 0) {
                alert("It's a tie!");
                game.state = "over";
            }
        }

        //Controls the opponent. Uses many nested switches/if-else for efficiency
        oppTurn = () => {
            if (game.state === "game") {
                let tile = 0;

                //Similar local variables as the win checker
                let at = tileArray[0].state;
                let bt = tileArray[1].state;
                let ct = tileArray[2].state;
                let dt = tileArray[3].state;
                let et = tileArray[4].state;
                let ft = tileArray[5].state;
                let gt = tileArray[6].state;
                let ht = tileArray[7].state;
                let it = tileArray[8].state;
                let all = [at, bt, ct, dt, et, ft, gt, ht, it];

                /*The AI will automatically win if possible
                I considered using a filter based system, but it was ugly and
                inelegant, and also redundant
                I used a nested if-else instead
                Equallen checks how many values in the array equal the given value*/
                if (equallen(all, "O") >= 2) {
                    if (equallen([at, bt, ct], "O") === 2 && equallen([at, bt, ct], "X") === 0) {
                        if (at === "none") {
                            tile = tileArray[0];
                        } else if (bt === "none") {
                            tile = tileArray[1];
                        } else if (ct === "none") {
                            tile = tileArray[2];
                        }
                    } else if (equallen([dt, et, ft], "O") === 2 && equallen([dt, et, ft], "X") === 0) {
                        if (dt === "none") {
                            tile = tileArray[3];
                        } else if (et === "none") {
                            tile = tileArray[4];
                        } else if (ft === "none") {
                            tile = tileArray[5];
                        }
                    } else if (equallen([gt, ht, it], "O") === 2 && equallen([gt, ht, it], "X") === 0) {
                        if (gt === "none") {
                            tile = tileArray[6];
                        } else if (ht === "none") {
                            tile = tileArray[7];
                        } else if (it === "none") {
                            tile = tileArray[8];
                        }
                    } else if (equallen([at, dt, gt], "O") === 2 && equallen([at, dt, gt], "X") === 0) {
                        if (at === "none") {
                            tile = tileArray[0];
                        } else if (dt === "none") {
                            tile = tileArray[3];
                        } else if (gt === "none") {
                            tile = tileArray[6];
                        }
                    } else if (equallen([bt, et, ht], "O") === 2 && equallen([bt, et, ht], "X") === 0) {
                        if (bt === "none") {
                            tile = tileArray[1];
                        } else if (et === "none") {
                            tile = tileArray[4];
                        } else if (ht === "none") {
                            tile = tileArray[7];
                        }
                    } else if (equallen([ct, ft, it], "O") === 2 && equallen([ct, ft, it], "X") === 0) {
                        if (ct === "none") {
                            tile = tileArray[2];
                        } else if (ft === "none") {
                            tile = tileArray[5];
                        } else if (it === "none") {
                            tile = tileArray[8];
                        }
                    } else if (equallen([at, et, it], "O") === 2 && equallen([at, et, it], "X") === 0) {
                        if (at === "none") {
                            tile = tileArray[0];
                        } else if (et === "none") {
                            tile = tileArray[4];
                        } else if (it === "none") {
                            tile = tileArray[8];
                        }
                    } else if (equallen([ct, et, gt], "O") === 2 && equallen([ct, et, gt], "X") === 0) {
                        if (ct === "none") {
                            tile = tileArray[2];
                        } else if (et === "none") {
                            tile = tileArray[4];
                        } else if (gt === "none") {
                            tile = tileArray[6];
                        }
                    }
                }

                //Stops player from winning if possible
                if (equallen(all, "X") >= 2 && tile === 0) {
                    if (equallen([at, bt, ct], "X") === 2 && equallen([at, bt, ct], "O") === 0) {
                        if (at === "none") {
                            tile = tileArray[0];
                        } else if (bt === "none") {
                            tile = tileArray[1];
                        } else if (ct === "none") {
                            tile = tileArray[2];
                        }
                    } else if (equallen([dt, et, ft], "X") === 2 && equallen([dt, et, ft], "O") === 0) {
                        if (dt === "none") {
                            tile = tileArray[3];
                        } else if (et === "none") {
                            tile = tileArray[4];
                        } else if (ft === "none") {
                            tile = tileArray[5];
                        }
                    } else if (equallen([gt, ht, it], "X") === 2 && equallen([gt, ht, it], "O") === 0) {
                        if (gt === "none") {
                            tile = tileArray[6];
                        } else if (ht === "none") {
                            tile = tileArray[7];
                        } else if (it === "none") {
                            tile = tileArray[8];
                        }
                    } else if (equallen([at, dt, gt], "X") === 2 && equallen([at, dt, gt], "O") === 0) {
                        if (at === "none") {
                            tile = tileArray[0];
                        } else if (dt === "none") {
                            tile = tileArray[3];
                        } else if (gt === "none") {
                            tile = tileArray[6];
                        }
                    } else if (equallen([bt, et, ht], "X") === 2 && equallen([bt, et, ht], "O") === 0) {
                        if (bt === "none") {
                            tile = tileArray[1];
                        } else if (et === "none") {
                            tile = tileArray[4];
                        } else if (ht === "none") {
                            tile = tileArray[7];
                        }
                    } else if (equallen([ct, ft, it], "X") === 2 && equallen([ct, ft, it], "O") === 0) {
                        if (ct === "none") {
                            tile = tileArray[2];
                        } else if (ft === "none") {
                            tile = tileArray[5];
                        } else if (it === "none") {
                            tile = tileArray[8];
                        }
                    } else if (equallen([at, et, it], "X") === 2 && equallen([at, et, it], "O") === 0) {
                        if (at === "none") {
                            tile = tileArray[0];
                        } else if (et === "none") {
                            tile = tileArray[4];
                        } else if (it === "none") {
                            tile = tileArray[8];
                        }
                    } else if (equallen([ct, et, gt], "X") === 2 && equallen([ct, et, gt], "O") === 0) {
                        if (ct === "none") {
                            tile = tileArray[2];
                        } else if (et === "none") {
                            tile = tileArray[4];
                        } else if (gt === "none") {
                            tile = tileArray[6];
                        }
                    }
                }

                //Other options in case the above are not fulfilled
                //Controls the course of play over the game
                if (tile === 0) {
                    switch (9 - available.length) {
                        case 1:
                            //If the center is taken, it plays randomly in the corner
                            //Otherwise, it takes the center
                            if (et === "X") {
                                tile = tileArray[choice([0, 2, 6, 8])];
                            } else {
                                tile = tileArray[4];
                            }
                            break;

                        case 3:
                            if (et === "X" && (equalanyof([at, ct, gt, it], "O"))) {
                                /*To counter the strategy of
                                    O - -
                                    - X -
                                    X - -

                                    O - -
                                    - X -
                                    - - X
                                and related strategies*/
                                if (at === "X") {
                                    if (it === "none") {
                                        tile = tileArray[8];
                                    } else {
                                        tile = tileArray[choice([2, 6])];
                                    }
                                } else if (ct === "X") {
                                    if (gt === "none") {
                                        tile = tileArray[6];
                                    } else {
                                        tile = tileArray[choice([0, 8])];
                                    }
                                } else if (gt === "X") {
                                    if (ct === "none") {
                                        tile = tileArray[2];
                                    } else {
                                        tile = tileArray[choice([0, 8])];
                                    }
                                } else if (it === "X") {
                                    if (at === "none") {
                                        tile = tileArray[0];
                                    } else {
                                        tile = tileArray[choice([2, 6])];
                                    }
                                }
                            } else {
                                tile = choice(tileArray);
                            }
                            break;
                    }
                }

                //Generates a random number if it could cause an error
                if (tile.state != "none") {
                    tile = choice(available);
                }

                //Draws the selection
                tile.state = game.opp;
                tile.draw();
                checkCondition();
                game.turn = "Player";
            }
        }

        //Click handler
        document.onclick = event => {
            let pos = getMousePos(event);

            switch (game.state) {
                case "start":
                    //Checks if the button was clicked
                    if (isBetween(pos.x, width / 3.2, width / (16 / 11)) && isBetween(pos.y, width / 2, width / 1.6)) {
                        game.state = "game"

                        //Draws the setup for the game
                        ctx.fillStyle = "rgb(225, 225, 225)";
                        ctx.fillRect(0, 0, 400, 400);

                        //Draws the lines
                        ctx.fillStyle = "rgb(200, 200, 200)";
                        ctx.fillRect(blockSize - lineSize / 2, 0, lineSize, width);
                        ctx.fillRect(blockSize * 2 - lineSize / 2, 0, lineSize, width);
                        ctx.fillRect(0, blockSize - lineSize / 2, width, lineSize);
                        ctx.fillRect(0, blockSize * 2 - lineSize / 2, width, lineSize);
                    }
                    break;

                case "game":
                    if (game.turn === "Player") {
                        //Goes through the tile array, checking if the click occurred there
                        tileArray.forEach(tile => {
                            if (isBetween(pos.x, tile.x, tile.x + blockSize) && isBetween(pos.y, tile.y, tile.y + blockSize)) {
                                if (available.indexOf(tile) != -1) {
                                    tile.state = game.player;
                                    tile.draw();
                                    checkCondition();
                                    game.turn = "Opponent";
                                    oppTurn();
                                }
                            }
                        });
                    }
                    break;
            }

        }
    </script>
</body>

</html>


const winningpositions = [[1, 2, 3], [4, 5, 6], [7, 8, 9], [1, 4, 7],
    [2, 5, 8], [3, 6, 9],[1, 5, 9], [7, 5, 3]]

function haswon(brd, xoro)
    marked = findall(x -> x == xoro, brd)
    for pos in winningpositions
        if length(pos) <= length(marked) && pos == sort(marked)[1:3]
            return true
        end
    end
    false
end

function readcharwithprompt(prompt, expected)
    ret = '*'
    while !(ret in expected)
        print("\n", prompt, " ->  ")
        ret = lowercase(chomp(readline()))[1]
    end
    ret
end

availablemoves(brd) = findall(x -> x == ' ', brd)
cornersopen(brd) = [x for x in [1, 3, 7, 9] if brd[x] == ' ']
int2char(x) = Char(x + UInt8('0'))
char2int(x) = UInt8(x) - UInt8('0')
getyn(query) = readcharwithprompt(query, ['y', 'n'])
gettheirmove(brd) = char2int(readcharwithprompt("Your move(1-9)", int2char.(availablemoves(brd))))

function findwin(brd, xoro)
    tmpbrd = deepcopy(brd)
    for mv in availablemoves(tmpbrd)
        tmpbrd[mv] = xoro
        if haswon(tmpbrd, xoro)
            return mv
        end
        tmpbrd[mv] = ' '
    end
    return nothing
end

function choosemove(brd, mychar, theirchar)
    if all(x -> x == ' ', brd)
        brd[rand(cornersopen(brd))] = mychar # corner trap if starting game
    elseif availablemoves(brd) == [] # no more moves
        println("Game is over. It was a draw.")
        exit(0)
    elseif (x = findwin(brd, mychar)) != nothing || (x = findwin(brd, theirchar)) != nothing
        brd[x] = mychar # win if possible, block their win otherwise if their win is possible
    elseif brd[5] == ' '
        brd[5] = mychar # take center if open and not doing corner trap
    elseif (corners = cornersopen(brd)) != []
        brd[rand(corners)] = mychar # choose a corner over a side middle move
    else
        brd[rand(availablemoves(brd))] = mychar # random otherwise
    end
end

function display(brd)
    println("+-----------+")
    println("| ", brd[1], " | ", brd[2], " | ", brd[3], " |")
    println("| ", brd[4], " | ", brd[5], " | ", brd[6], " |")
    println("| ", brd[7], " | ", brd[8], " | ", brd[9], " |")
    println("+-----------+")
end

function tictactoe()
    board = fill(' ', 9)
    println("Board move grid:\n 1 2 3\n 4 5 6\n 7 8 9")
    yn = getyn("Would you like to move first (y/n)?")
    if yn == 'y'
        mychar = 'O'
        theirchar = 'X'
        board[gettheirmove(board)] = theirchar
    else
        mychar = 'X'
        theirchar = 'O'
    end
    while true
        choosemove(board, mychar, theirchar)
        println("Computer has moved.")
        display(board)
        if haswon(board, mychar)
            println("Game over. Computer wins!")
            exit(0)
        elseif availablemoves(board) == []
            break
        end
        board[gettheirmove(board)] = theirchar
        println("Player has moved.")
        display(board)
        if haswon(board, theirchar)
            println("Game over. Player wins!")
            exit(0)
        elseif availablemoves(board) == []
            break
        end
    end
    println("Game over. It was a draw.")
end

tictactoe()


// version 1.1.51

import java.util.Random

val r = Random()
val b = Array(3) { IntArray(3) }  // board -> 0: blank; -1: computer; 1: human

var bestI = 0
var bestJ = 0

fun checkWinner(): Int {
    for (i in 0..2) {
        if (b[i][0] != 0 && b[i][1] == b[i][0] && b[i][2] == b[i][0]) return b[i][0]
        if (b[0][i] != 0 && b[1][i] == b[0][i] && b[2][i] == b[0][i]) return b[0][i]
    }
    if (b[1][1] == 0) return 0
    if (b[1][1] == b[0][0] && b[2][2] == b[0][0]) return b[0][0]
    if (b[1][1] == b[2][0] && b[0][2] == b[1][1]) return b[1][1]
    return 0
}

fun showBoard() {
    val t = "X O"
    for (i in 0..2) {
        for (j in 0..2) print("${t[b[i][j] + 1]} ")
        println()
    }
    println("-----")
}

fun testMove(value: Int, depth: Int): Int {
    var best = -1
    var changed = 0
    var score = checkWinner()
    if (score != 0) return if (score == value) 1 else -1
    for (i in 0..2) {
        for (j in 0..2) {
            if (b[i][j] != 0) continue
            b[i][j] = value
            changed = value
            score = -testMove(-value, depth + 1)
            b[i][j] = 0
            if (score <= best) continue
            if (depth == 0) {
                bestI = i
                bestJ = j
            }
            best = score
        }
    }
    return if (changed != 0) best else 0
}

fun game(user: Boolean): String {
    var u = user
    for (i in 0..2) b[i].fill(0)
    print("Board postions are numbered so:\n1 2 3\n4 5 6\n7 8 9\n")
    print("You have O, I have X.\n\n")

    for (k in 0..8) {
        while (u) {
            var move: Int?
            do {
                print("Your move: ")
                move = readLine()!!.toIntOrNull()
            }
            while (move != null && move !in 1..9)
            move = move!! - 1
            val i = move / 3
            val j = move % 3
            if (b[i][j] != 0) continue
            b[i][j] = 1
            break
        }
        if (!u) {
            if (k == 0) { // randomize if computer opens, less boring
                bestI = r.nextInt(Int.MAX_VALUE) % 3
                bestJ = r.nextInt(Int.MAX_VALUE) % 3
            }
            else testMove(-1, 0)
            b[bestI][bestJ] = -1
            val myMove = bestI * 3 + bestJ + 1
            println("My move: $myMove")
        }
        showBoard()
        val win = checkWinner()
        if (win != 0) return (if (win == 1) "You win" else "I win") + ".\n\n"
        u = !u
    }
    return "A draw.\n\n"
}

fun main(args: Array<String>) {
    var user = false
    while (true) {
        user = !user
        print(game(user))
        var yn: String
        do {
            print("Play again y/n: ")
            yn = readLine()!!.toLowerCase()
        }
        while (yn != "y" && yn != "n")
        if (yn != "y") return
        println()
    }
}


[
session_start('user')
session_addvar('user', 'matrix')
session_addvar('user', 'winrecord')
session_addvar('user', 'turn')
var(matrix)->isNotA(::array) ? var(matrix = array('-','-','-','-','-','-','-','-','-'))
var(winrecord)->isNotA(::array) ? var(winrecord = array)
var(turn)->isNotA(::string) ? var(turn = 'x')

if(web_request->params->asStaticArray >> 'reset') => {
	$matrix = array('-','-','-','-','-','-','-','-','-')
	$turn = 'x'
}

with i in web_request->params->asStaticArray do => {
	if(#i->name->beginswith('p')) => {
		local(num = #i->name->asCopy)
		#num->removeLeading('p')
		#num = integer(#num)
		#num > 0 && $matrix->get(#num) == '-' ? $matrix->get(#num) = #i->value
		$turn == 'o' ? $turn = 'x' | $turn = 'o'
	}
}

local(
	istie 	= false,
	winner	= 'noone',
	clear	= false
)

// determine if we have a winner
if($matrix->find('-')->size < 9) => {
	local(winners = array('123','456','789','147','258','369','159','357'))
	loop(8) => {
		local(xscore = 0,oscore = 0,use = #winners->get(loop_count))
		with v in #use->values do => {
			$matrix->findposition('x') >> integer(#v) ? #xscore++
			$matrix->findposition('o') >> integer(#v) ? #oscore++
		}
		if(#xscore == 3) => {
			#winner = 'x'
			$winrecord->insert('x')
			#clear = true
			loop_abort
		}
		if(#oscore == 3) => {
			#winner = 'o'
			$winrecord->insert('o')
			#clear = true
			loop_abort
		}

	}

}
// determine if tie
if(not $matrix->find('-')->size && #winner == 'noone') => {
	#istie = true
	#winner = 'tie'
	$winrecord->insert('tie')
	#clear = true
}
]
<form action="?" method="post">

      [loop(3) => {^]<button name="p[loop_count]" value="[$turn]"[
        $matrix->get(loop_count) != '-' || #winner != 'noone' ? ' disabled="disabled"'
      ]>[$matrix->get(loop_count) != '-' ? $matrix->get(loop_count) | ' ']</button>	[^}]

      [loop(-from=4,-to=6) => {^]<button name="p[loop_count]" value="[$turn]"[
        $matrix->get(loop_count) != '-' || #winner != 'noone' ? ' disabled="disabled"'
      ]>[$matrix->get(loop_count) != '-' ? $matrix->get(loop_count) | ' ']</button>	[^}]

      [loop(-from=7,-to=9) => {^]<button name="p[loop_count]" value="[$turn]"[
        $matrix->get(loop_count) != '-' || #winner != 'noone' ? ' disabled="disabled"'
      ]>[$matrix->get(loop_count) != '-' ? $matrix->get(loop_count) | ' ']</button>	[^}]

</form>
[if(#istie && #winner == 'tie')]

It's a tie!

[else(#winner != 'noone')]

[#winner->uppercase&] won! Congratulations.

[else]<math>Insert formula here</math>

It is now [$turn]'s turn!

[/if]

Reset

[if($winrecord->size)]
Win record: [$winrecord->join(', ')]
[/if]
[if(#clear == true) => {
	$matrix = array('-','-','-','-','-','-','-','-','-')
	$turn = 'x'
}]


global $ -- object representing simple framework
global gBoard -- current board image
global gBoardTemplate -- empty board image
global gHumanChip -- cross image
global gComputerChip -- circle image
global gM -- 3x3 matrix storing game state: 0=free cell, 1=human cell, -1=computer cell
global gStep -- index of current move (1..9)
global gGameOverFlag -- TRUE if current game is over

----------------------------------------
-- Entry point
----------------------------------------
on startMovie

    -- libs
    $.import("sprite")

    -- window properties
    _movie.stage.title = "Tic-Tac-Toe"
    _movie.stage.rect = rect(0, 0, 224, 310)
    _movie.centerStage = TRUE

    -- load images from filesystem
    m = new(#bitmap)
    m.importFileInto([email protected]("resources/cross.bmp"), [#trimWhiteSpace:FALSE])
    gHumanChip = m.image

    m = new(#bitmap)
    m.importFileInto([email protected]("resources/circle.bmp"), [#trimWhiteSpace:FALSE])
    gComputerChip = m.image

    -- create GUI
    m = new(#bitmap)
    m.importFileInto([email protected]("resources/board.bmp"))
    m.regpoint = point(0, 0)
    s = $.sprite.make(m, [#loc:point(20, 20)], TRUE)
    s.addListener(#mouseDown, _movie, #humanMove)

    gBoard = m.image
    gBoardTemplate = gBoard.duplicate()

    m = $.sprite.newMember(#button, [#text:"New Game (Human starts)", #fontstyle:"bold", #rect:rect(0, 0, 180, 0)])
    s = $.sprite.make(m, [#loc:point(20, 220)], TRUE)
    s.addListener(#mouseDown, _movie, #newGame, 1)

    m = $.sprite.newMember(#button, [#text:"New Game (Computer starts)", #fontstyle:"bold", #rect:rect(0, 0, 180, 0)])
    s = $.sprite.make(m, [#loc:point(20, 250)], TRUE)
    s.addListener(#mouseDown, _movie, #newGame, -1)

    m = $.sprite.newMember(#field, [#name:"feedback", #editable:FALSE, #fontstyle:"bold", #alignment:"center",\
        #border:0, #color:rgb(255, 0, 0), #rect:rect(0, 0, 180, 0)])
    s = $.sprite.make(m, [#loc:point(20, 280)], TRUE)

    newGame(1)

    -- show the application window
    _movie.updateStage()
    _movie.stage.visible = TRUE
end

----------------------------------------
-- Starts a new game
----------------------------------------
on newGame (whoStarts)
    -- reset board
    gBoard.copyPixels(gBoardTemplate, gBoardTemplate.rect, gBoardTemplate.rect)
    -- clear feedback
    member("feedback").text = ""
    -- reset states
    gM = [[0, 0, 0], [0, 0, 0], [0, 0, 0]]
    gStep = 0
    gGameOverFlag = FALSE
    if whoStarts=-1 then computerMove()
end

----------------------------------------
-- Handles a human move (mouse click)
----------------------------------------
on humanMove ()
    if gGameOverFlag then return
    -- find cell for mouse position
    p = _mouse.clickLoc - sprite(1).loc
    if p.locH mod 60<4 or p.locV mod 60<4 then return
    p = p / 60
    x = p[1] + 1
    y = p[2] + 1
    if gM[x][y] then return -- ignore illegal moves
    gM[x][y] = 1
    -- update cell image
    p = p * 60
    gBoard.copyPixels(gHumanChip, gHumanChip.rect.offset(4+p[1], 4+p[2]), gHumanChip.rect)
    -- proceed (unless game over)
    gStep = gStep + 1
    if not checkHumanMove(x, y) then computerMove()
end

----------------------------------------
-- Checks if human has won or game ended with draw
----------------------------------------
on checkHumanMove (x, y)
    if sum([gM[x][1], gM[x][2], gM[x][3]])=3 then return gameOver(1, [[x, 1], [x, 2], [x, 3]])
    if sum([gM[1][y], gM[2][y], gM[3][y]])=3 then return gameOver(1, [[1, y], [2, y], [3, y]])
    if x=y and sum([gM[1][1], gM[2][2], gM[3][3]])=3 then return gameOver(1, [[1, 1], [2, 2], [3, 3]])
    if x+y=4 and sum([gM[1][3], gM[2][2], gM[3][1]])=3 then return gameOver(1, [[1, 3], [2, 2], [3, 1]])
    if gStep=9 then return gameOver(0)
    return FALSE
end

----------------------------------------
-- Checks if selecting specified empty cell makes computer or human win
----------------------------------------
on checkCellWins (x, y, who)
    wins = who*2
    if sum([gM[1][y], gM[2][y], gM[3][y]]) = wins then return [[1, y], [2, y], [3, y]]
    if sum([gM[x][1], gM[x][2], gM[x][3]]) = wins then return [[x, 1], [x, 2], [x, 3]]
    if x=y and sum([gM[1][1], gM[2][2], gM[3][3]]) = wins then return [[1, 1], [2, 2], [3, 3]]
    if x+y=4 and sum([gM[1][3], gM[2][2], gM[3][1]]) = wins then return [[1, 3], [2, 2], [3, 1]]
    return FALSE
end

----------------------------------------
-- Handles game over
----------------------------------------
on gameOver (winner, cells)
    gGameOverFlag = TRUE
    if winner = 0 then
        member("feedback").text = "It's a draw!"
    else
        -- hilite winning line with yellow
        img = image(56, 56, 32)
        img.fill(img.rect, rgb(255, 255, 0))
        repeat with c in cells
            x = (c[1]-1)*60 + 4
            y = (c[2]-1)*60 + 4
            gBoard.copyPixels(img, img.rect.offset(x, y), img.rect, [#ink:#darkest])
        end repeat
        member("feedback").text = ["Human", "Computer"][1+(winner=-1)] & " has won!"
    end if
    return TRUE
end

----------------------------------------
-- Calculates next computer move
----------------------------------------
on computerMove ()
    gStep = gStep + 1

    -- move 1: select center
    if gStep=1 then return doComputerMove(2, 2)

    -- move 2 (human started)
    if gStep=2 then
        if gM[2][2]=1 then
            -- if center, select arbitrary corner
            return doComputerMove(1, 1)
        else
            -- otherwise select center
            return doComputerMove(2, 2)
        end if
    end if

    -- move 3 (computer started)
    if gStep=3 then
        -- if corner, select diagonally opposite corner
        if gM[1][1]=1 then return doComputerMove(3, 3)
        if gM[3][3]=1 then return doComputerMove(1, 1)
        if gM[1][3]=1 then return doComputerMove(3, 1)
        return doComputerMove(1, 1) -- top left corner as default
    end if

    -- get free cells
    free = []
    repeat with x = 1 to 3
        repeat with y = 1 to 3
            if gM[x][y]=0 then free.add([x, y])
        end repeat
    end repeat

    -- check if computer can win now
    repeat with c in free
        res = checkCellWins(c[1], c[2], -1)
        if res<>FALSE then
            doComputerMove(c[1], c[2])
            return gameOver(-1, res)
        end if
    end repeat

    -- check if human could win with next move (if yes, prevent it)
    repeat with c in free
        res = checkCellWins(c[1], c[2], 1)
        if res<>FALSE then return doComputerMove(c[1], c[2], TRUE)
    end repeat

    -- move 4 (human started): prevent "double mills"
    if gStep=4 then
        if gM[2][2]=1 and (gM[1][1]=1 or gM[3][3]=1) then return doComputerMove(3, 1)
        if gM[2][2]=1 and (gM[1][3]=1 or gM[3][1]=1) then return doComputerMove(1, 1)
        if gM[2][3]+gM[3][2]=2 then return doComputerMove(3, 3)
        if gM[1][2]+gM[2][3]=2 then return doComputerMove(1, 3)
        if gM[1][2]+gM[2][1]=2 then return doComputerMove(1, 1)
        if gM[2][1]+gM[3][2]=2 then return doComputerMove(3, 1)
        if (gM[1][3]+gM[3][1]=2) or (gM[1][1]+gM[3][3]=2) then return doComputerMove(2, 1)
    end if

    -- move 5 (computer started): try to create a "double mill"
    if gStep=5 then
        repeat with x = 1 to 3
            col = [gM[x][1], gM[x][2], gM[x][3]]
            if not (sum(col)=-1 and max(col)=0) then next repeat
            repeat with y = 1 to 3
                row = [gM[1][y], gM[2][y], gM[3][y]]
                if not (sum(row)=-1 and max(row)=0 and gM[x][y]=0) then next repeat
                return doComputerMove(x, y)
            end repeat
        end repeat
    end if

    -- otherwise use first free cell
    c = free[1]
    doComputerMove(c[1], c[2])
end

----------------------------------------
-- Updates state matrix and cell image
----------------------------------------
on doComputerMove (x, y, checkDraw)
    gM[x][y] = -1
    gBoard.copyPixels(gComputerChip, gComputerChip.rect.offset(4+(x-1)*60, 4+(y-1)*60), gComputerChip.rect)
    if checkDraw and gStep=9 then gameOver(0)
end

----------------------------------------
--
----------------------------------------
on sum (aLine)
    return aLine[1]+aLine[2]+aLine[3]
end


#!/usr/bin/env luajit
ffi=require"ffi"
local function printf(fmt,...) io.write(string.format(fmt, ...)) end
local board="123456789" -- board
local pval={1, -1}      -- player 1=1 2=-1 for negamax
local pnum={} for k,v in ipairs(pval) do pnum[v]=k end
local symbol={'X','O'}  -- default symbols X and O
local isymbol={} for k,v in pairs(symbol) do isymbol[v]=pval[k] end
math.randomseed(os.time()^5*os.clock()) -- time-seed the random gen
local random=math.random
-- usage of ffi variables give 20% speed
ffi.cdef[[
	typedef struct{
		char value;
		char flag;
		int depth;
	}cData;
]]
-- draw the "board" in the way the numpad is organized
local function draw(board)
	for i=7,1,-3 do
		print(board:sub(i,i+2))
	end
end
-- pattern for win situations
local check={"(.)...%1...%1","..(.).%1.%1..",
	"(.)%1%1......","...(.)%1%1...","......(.)%1%1",
	"(.)..%1..%1..",".(.)..%1..%1.","..(.)..%1..%1",
}
-- calculate a win situation for which player or draw
local function win(b)
	local sub
	for i=1,#check do
		sub=b:match(check[i])
		if sub then break end
	end
	sub=isymbol[sub]
	return sub or 0
end
-- input only validate moves of not yet filled positions
local function input(b,player)
	char=symbol[pnum[player]]
	local inp
	repeat
		printf("Player %d (\"%s\") move: ",pnum[player],char)
		inp=tonumber(io.read()) or 0
	until inp>=1 and inp<=9 and b:find(inp)
	b=b:gsub(inp,char)
	return b,inp
end
-- ask how many human or AI players
local function playerselect()
	local ai={}
	local yn
	for i=1,2 do
		repeat
			printf("Player %d human (Y/n)? ", i) yn=io.read():lower()
		until yn:match("[yn]") or yn==''
		if yn=='n' then
			ai[pval[i]]=true
			printf("Player %d is AI\n", i)
		else
			printf("Player %d is human\n", i)
		end
	end
	return ai
end
local function endgame()
	repeat
		printf("\nEnd game? (y/n)? ", i) yn=io.read():lower()
	until yn:match("[yn]")
	if yn=='n' then
		return false
	else
		printf("\nGOOD BYE PROFESSOR FALKEN.\n\nA STRANGE GAME.\nTHE ONLY WINNING MOVE IS\nNOT TO PLAY.\n\nHOW ABOUT A NICE GAME OF CHESS?\n")
		return true
	end
end
-- AI Routine
local function shuffle(t)
	for i=#t,1,-1 do
		local rnd=random(i)
		t[i], t[rnd] = t[rnd], t[i]
	end
	return t
end
-- move generator
local function genmove(node, color)
	return coroutine.wrap(function()
		local moves={}
		for m in node:gmatch("%d") do
			moves[#moves+1]=m
		end
		shuffle(moves) -- to make it more interesting
		for _,m in ipairs(moves) do
			local child=node:gsub(m,symbol[pnum[color]])
			coroutine.yield(child, m)
		end
	end)
end
--[[
Negamax with alpha-beta pruning and table caching
]]
local cache={}
local best, aimove, tDepth
local LOWERB,EXACT,UPPERB=-1,0,1 -- has somebody an idea how to make them real constants?
local function negamax(node, depth, color, α, β)
	color=color or 1
	α=α or -math.huge
	β=β or math.huge
	-- check for cached node
	local αOrg=α
	local cData=cache[node]
	if cData and cData.depth>=depth and depth~=tDepth then
		if cData.flag==EXACT then
			return cData.value
		elseif cData.flag==LOWERB then
			α=math.max(α,cData.value)
		elseif cData.flag==UPPERB then
			β=math.min(β,cData.value)
		end
		if α>=β then
			return cData.value
		end
	end

	local winner=win(node)
	if depth==0 or winner~=0 then
		return winner*color
	end
	local value=-math.huge
	for child,move in genmove(node, color) do
		value=math.max(value, -negamax(child, depth-1, -color, -β, -α))
		if value>α then
			α=value
			if depth==tDepth then
				best=child
				aimove=move
			end
		end
		if α>=β then break end
	end
	-- cache known data
	--cData={}  -- if you want Lua tables instead of ffi you can switch the two lines here, costs 20% speed
	cData=ffi.new("cData")
	cData.value=value
	if value<=αOrg then
		cData.flag=UPPERB
	elseif value>=β then
		cData.flag=LOWERB
	else
		cData.flag=EXACT
	end
	cData.depth=depth
	cache[node]=cData
	return α
end
-- MAIN
do
	local winner,value
	local score={[-1]=0, [0]=0, [1]=0}
	repeat
		print("\n   TIC-TAC-TOE\n")
		local aiplayer=playerselect()
		local player=1
		board="123456789"
		for i=1,#board do
			draw(board)
			tDepth=10-i
			if aiplayer[player] then
				negamax(board, tDepth, player, -math.huge, math.huge)
				board=best
				printf("AI %d moves %s\n", pnum[player], aimove)
			else
				board=input(board,player)
			end
			winner=win(board)
			if winner~=0 then break end
			player=-player
		end
		score[winner]=score[winner]+1
		if winner and winner~=0 then
			printf("*** Player %d (%s) has won\n", pnum[winner], symbol[pnum[winner]])
		else
			printf("*** No winner\n")
		end
		printf("Score Player 1: %d Player 2: %d Draw: %d\n",score[1],score[-1],score[0])
		draw(board)
	until endgame()
end


Module Tic.Tac.Toe {
      Dim Board$(1 to 3, 1 to 3)=" "
      WinGame=False
      p=Board$()
      RandomPosition=lambda -> {
            =(random(1,3), random(1,3))
      }

      BoardItemEmpty=Lambda p (x, y) -> {
            =Array$(p, x, y)=" "
      }
      BoardSetItem=Lambda p (x, y, w$) -> {
            link p to a$()
            a$(x, y)=w$
      }
      T=9
      R=0
      C=0
      Repeat {
            Print "Computer Move:"
            CompMove()
            T--
            DrawBoard()
            CheckWin()
            if WinGame Then Print "Computer Win": Exit
            if T=0 then exit
            Repeat {
                  GetRowCol("Input Row", &R)
                  GetRowCol("Input Column", &C)
                  If BoardItemEmpty(R,C) then call boardsetitem(R,C,"O") : exit
            } Always
            T--
            DrawBoard()
            CheckWin()
            if WinGame Then Print "You Win": Exit
      } until T=0 or WinGame
      Sub DrawBoard()
      Print "R/C 1 2 3"
      Print " 1) "; Board$(1,1);"|";Board$(1,2);"|";Board$(1,3)
      Print "    -+-+-"
      Print " 2) "; Board$(2,1);"|";Board$(2,2);"|";Board$(2,3)
      Print "    -+-+-"
      Print " 3) "; Board$(3,1);"|";Board$(3,2);"|";Board$(3,3)
      End Sub
      Sub CheckWin()
            WinGame=false
            local i,j,three$
            For i=1 to 3
                  three$=""
                  For j=1 to 3 : three$+=Board$(i,j) : Next j
                  CheckThree()
                  three$=""
                  For j=1 to 3 :  three$+=Board$(j,i) :Next j
                  CheckThree()
            Next i
            three$=""
            For i=1 to 3 : three$+=Board$(i,i): Next i
            CheckThree()
            three$=""
            For i=1 to 3:three$+=Board$(i,4-i): Next i
            CheckThree()
      End Sub
      Sub CheckThree()
            if instr(three$," ")=0 then  WinGame=WinGame or Filter$(three$, left$(three$,1))=""
      End Sub
      Sub CompMove()
            if T<9 and Board$(2,2)=" " then {
                  call boardsetitem(2,2,"X")
            } Else {
                  local i=3, j=3, found=false
                  if T<=6 then {
                        CompThink("X","X")
                  }
                  let i=3, j=3
                  If Not found And T<6 then {
                        CompThink("O","X")
                  }
                  If not found then {
                        Repeat {
                              comp=RandomPosition()
                              If BoardItemEmpty(!comp) then call boardsetitem(!comp, "X") : exit
                        } Always
                  }
            }
      End Sub
      Sub CompThink(Bad$, Good$)
                        While i>0 {
                              j=3
                              While j>0 {
                                    if Board$(i,j)=" " then {
                                          Board$(i,j)=Bad$
                                          CheckWin()
                                          if WinGame then {
                                                 Board$(i,j)=Good$:i=0:j=0: found=true
                                          } Else Board$(i,j)=" "
                                    }
                                    j--
                              }
                              i--
                        }

      End Sub
      Sub GetRowCol(What$, &W)
            Print What$;":";
            Repeat {
                  W=Val("0"+Key$)
            } until W>=1 and W<=3
            Print Str$(W,"")
      End Sub
}
Tic.Tac.Toe


DynamicModule[{board = ConstantArray[0, {3, 3}], text = "Playing...",
  first, rows =
   Join[#, [email protected]#, {[email protected]#, [email protected]@#}] &},
 [email protected]{Graphics[{Thickness[.02],
     Table[With[{i = i, j = j},
       Button[{White, Rectangle[{i, j} - 1, {i, j}], Black,
         Dynamic[Switch[board[[i, j]], 0, Black, 1,
           Circle[{i, j} - .5, .3], -1,
           Line[{{{i, j} - .2, {i, j} - .8}, {{i - .2,
               j - .8}, {i - .8, j - .2}}}]]]},
        Which[text != "Playing...", board = ConstantArray[0, {3, 3}];
         text = "Playing...", board[[i, j]] == 0,
         If[board == ConstantArray[0, {3, 3}],
          first = {i,
             j} /. {{2, 2} -> 1, {1 | 3, 1 | 3} -> 2, _ -> 3}];
         board[[i, j]] = 1;
         FinishDynamic[];
         Which[MemberQ[rows[board], {1, 1, 1}], text = "You win.",
          FreeQ[board, 0], text = "Draw.", True,
          board[[Sequence @@
              SortBy[Select[Tuples[{[email protected], [email protected]}],
                 board[[Sequence @@ #]] ==
                   0 &], -Total[
                    Sort /@
                    rows[ReplacePart[
                    board, # -> -1]] /. {{-1, -1, -1} ->
                    512, {-1, 1, 1} -> 64, {-1, -1, 0} ->
                    8, {0, 1, 1} -> -1, {_, _, _} -> 0}] -
                  Switch[#, {2, 2}, 1, {1 | 3, 1 | 3},
                   If[first == 2, -1, 0], _,
                   If[first == 2, 0, -1]] &][[1]]]] = -1;
          Which[MemberQ[rows[board], {-1, -1, -1}],
           text = "You lost.", FreeQ[board, 0],
           text = "Draw."]]]]], {i, 1, 3}, {j, 1, 3}], Thickness[.01],
      Line[{{{1, 0}, {1, 3}}, {{2, 0}, {2, 3}}, {{0, 1}, {3, 1}}, {{0,
          2}, {3, 2}}}]}], [email protected]}]


function TicTacToe

    % Set up the board (one for each player)
    boards = false(3, 3, 2);    % Players' pieces
    rep = ['   1 | 4 | 7' ; '   2 | 5 | 8' ; '   3 | 6 | 9'];

    % Prompt user with options
    fprintf('Welcome to Tic-Tac-Toe!\n')
    nHumans = str2double(input('Enter the number of human players: ', 's'));
    if isnan(nHumans) || ceil(nHumans) ~= nHumans || nHumans < 1 || nHumans > 2
        nHumans = 0;
        pHuman = false(2, 1);
    elseif nHumans == 1
        humanFirst = input('Would the human like to go first (Y/N)? ', 's');
        if length(humanFirst) == 1 && lower(humanFirst) == 'n'
            pHuman = [false ; true];
        else
            pHuman = [true ; false];
        end
    else
        pHuman = true(2, 1);
    end
    if any('o' == input('Should Player 1 use X or O? ', 's'))
        marks = 'OX';
    else
        marks = 'XO';
    end
    fprintf('So Player 1 is %shuman and %cs and Player 2 is %shuman and %cs.\n', ...
        char('not '.*~pHuman(1)), marks(1), char('not '.*~pHuman(2)), marks(2))
    if nHumans > 0
        fprintf('Select the space to mark by entering the space number.\n')
        fprintf('No entry will quit the game.\n')
    end

    % Play game
    gameOver = false;
    turn = 1;
    while ~gameOver
        fprintf('\n')
        disp(rep)
        fprintf('\n')
        if pHuman(turn)
            [move, isValid, isQuit] = GetMoveFromPlayer(turn, boards);
            gameOver = isQuit;
        else
            move = GetMoveFromComputer(turn, boards);
            fprintf('Player %d chooses %d\n', turn, move)
            isValid = true;
            isQuit = false;
        end
        if isValid && ~isQuit
            [r, c] = ind2sub([3 3], move);
            boards(r, c, turn) = true;
            rep(r, 4*c) = marks(turn);
            if CheckWin(boards(:, :, turn))
                gameOver = true;
                fprintf('\n')
                disp(rep)
                fprintf('\nPlayer %d wins!\n', turn)
            elseif CheckDraw(boards)
                gameOver = true;
                fprintf('\n')
                disp(rep)
                fprintf('\nCat''s game!\n')
            end
            turn = ~(turn-1)+1;
        end
    end
end

function [move, isValid, isQuit] = GetMoveFromPlayer(pNum, boards)
% move - 1-9 indicating move position, 0 if invalid move
% isValid - logical indicating if move was valid, true if quitting
% isQuit - logical indicating if player wishes to quit game
    p1 = boards(:, :, 1);
    p2 = boards(:, :, 2);
    moveStr = input(sprintf('Player %d: ', pNum), 's');
    if isempty(moveStr)
        fprintf('Play again soon!\n')
        move = 0;
        isValid = true;
        isQuit = true;
    else
        move = str2double(moveStr);
        isQuit = false;
        if isnan(move) || move < 1 || move > 9 || p1(move) || p2(move)
            fprintf('%s is an invalid move.\n', moveStr)
            isQuit = 0;
            isValid = false;
        else
            isValid = true;
        end
    end
end

function move = GetMoveFromComputer(pNum, boards)
% pNum - 1-2 player number
% boards - 3x3x2 logical array where pBoards(:,:,1) is player 1's marks
% Assumes that it is possible to make a move
    if ~any(boards(:))     % Play in the corner for first move
        move = 1;
    else                    % Use Newell and Simon's "rules to win"
        pMe = boards(:, :, pNum);
        pThem = boards(:, :, ~(pNum-1)+1);
        possMoves = find(~(pMe | pThem)).';

        % Look for a winning move
        move = FindWin(pMe, possMoves);
        if move
            return
        end

        % Look to block opponent from winning
        move = FindWin(pThem, possMoves);
        if move
            return
        end

        % Look to create a fork (two non-blocked lines of two)
        for m = possMoves
            newPMe = pMe;
            newPMe(m) = true;
            if CheckFork(newPMe, pThem)
                move = m;
                return
            end
        end

        % Look to make two in a row so long as it doesn't force opponent to fork
        notGoodMoves = false(size(possMoves));
        for m = possMoves
            newPMe = pMe;
            newPMe(m) = true;
            if CheckPair(newPMe, pThem)
                nextPossMoves = possMoves;
                nextPossMoves(nextPossMoves == m) = [];
                theirMove = FindWin(newPMe, nextPossMoves);
                newPThem = pThem;
                newPThem(theirMove) = true;
                if ~CheckFork(newPThem, newPMe)
                    move = m;
                    return
                else
                    notGoodMoves(possMoves == m) = true;
                end
            end
        end
        possMoves(notGoodMoves) = [];

        % Play the center if available
        if any(possMoves == 5)
            move = 5;
        	return
        end

        % Play the opposite corner of the opponent's piece if available
        corners = [1 3 7 9];
        move = intersect(possMoves, ...
            corners(~(pMe(corners) | pThem(corners)) & pThem(fliplr(corners))));
        if ~isempty(move)
            move = move(1);
            return
        end

        % Play an empty corner if available
        move = intersect(possMoves, corners);
        if move
            move = move(1);
            return
        end

        % Play an empty side if available
        sides = [2 4 6 8];
        move = intersect(possMoves, sides);
        if move
            move = move(1);
            return
        end

        % No good moves, so move randomly
        possMoves = find(~(pMe | pThem));
        move = possMoves(randi(length(possMoves)));
    end
end

function move = FindWin(board, possMoves)
% board - 3x3 logical representing one player's pieces
% move - integer indicating position to move to win, or 0 if no winning move
    for m = possMoves
        newPMe = board;
        newPMe(m) = true;
        if CheckWin(newPMe)
            move = m;
            return
        end
    end
    move = 0;
end

function win = CheckWin(board)
% board - 3x3 logical representing one player's pieces
% win - logical indicating if that player has a winning board
    win = any(all(board)) || any(all(board, 2)) || ...
        all(diag(board)) || all(diag(fliplr(board)));
end

function fork = CheckFork(p1, p2)
% fork - logical indicating if player 1 has created a fork unblocked by player 2
    fork = sum([sum(p1)-sum(p2) (sum(p1, 2)-sum(p2, 2)).' ...
        sum(diag(p1))-sum(diag(p2)) ...
        sum(diag(fliplr(p1)))-sum(diag(fliplr(p2)))] == 2) > 1;
end

function pair = CheckPair(p1, p2)
% pair - logical indicating if player 1 has two in a line unblocked by player 2
    pair = any([sum(p1)-sum(p2) (sum(p1, 2)-sum(p2, 2)).' ...
        sum(diag(p1))-sum(diag(p2)) ...
        sum(diag(fliplr(p1)))-sum(diag(fliplr(p2)))] == 2);
end

function draw = CheckDraw(boards)
% boards - 3x3x2 logical representation of all players' pieces
    draw = all(all(boards(:, :, 1) | boards(:, :, 2)));
end


import options, random, sequtils, strutils

type
  Board = array[1..9, char]
  Score = (char, array[3, int])

const NoChoice = 0

var board: Board = ['1', '2', '3', '4', '5', '6', '7', '8', '9']

const Wins = [[1, 2, 3], [4, 5, 6], [7, 8, 9],
              [1, 4, 7], [2, 5, 8], [3, 6, 9],
              [1, 5, 9], [3, 5, 7]]

template toIndex(ch: char): int =
  ## Convert a character to an index in board.
  ord(ch) - ord('0')

proc print(board: Board) =
  for i in [1, 4, 7]:
    echo board[i..(i + 2)].join(" ")

proc score(board: Board): Option[Score] =
  for w in Wins:
    let b = board[w[0]]
    if b in "XO" and w.allIt(board[it] == b):
      return some (b, w)
  result = none(Score)

proc finished(board: Board): bool =
  board.allIt(it in "XO")

proc space(board: Board): seq[char] =
  for b in board:
    if b notin "XO":
      result.add b

proc myTurn(board: var Board; xo: char): char =
  let options = board.space()
  result = options.sample()
  board[result.toIndex] = xo

proc myBetterTurn(board: var Board; xo: char): int =
  let ox = if xo == 'X': 'O' else: 'X'
  var oneBlock = NoChoice
  let options = board.space.mapIt(it.toIndex)
  block search:
    for choice in options:
      var brd = board
      brd[choice] = xo
      if brd.score.isSome:
        result = choice
        break search
      if oneBlock == NoChoice:
        brd[choice] = ox
        if brd.score.isSome:
          oneBlock = choice
    result = if oneBlock != NoChoice: oneBlock else: options.sample()
  board[result] = xo

proc yourTurn(board: var Board; xo: char): int =
  let options = board.space()
  var choice: char
  while true:
    stdout.write "\nPut your $# in any of these positions: $# ".format(xo, options.join())
    let input = stdin.readLine().strip()
    if input.len == 1 and input[0] in options:
      choice = input[0]
      break
    echo "Whoops I don't understand the input"
  result = choice.toIndex
  board[result] = xo

proc me(board: var Board; xo: char): Option[Score] =
  board.print()
  echo "\nI go at ", board.myBetterTurn(xo)
  result = board.score()

proc you(board: var Board; xo: char): Option[Score] =
  board.print()
  echo "\nYou went at ", board.yourTurn(xo)
  result = board.score()

proc play() =
  while not board.finished():
    let score = board.me('X')
    if score.isSome:
      board.print()
      let (winner, line) = score.get()
      echo "\n$# wins along ($#).".format(winner, line.join(", "))
      return
    if not board.finished():
      let score = board.you('O')
      if score.isSome:
        board.print()
        let (winner, line) = score.get()
        echo "\n$# wins along ($#).".format(winner, line.join(", "))
        return
  echo "\nA draw."

echo "Tic-tac-toe game player."
echo "Input the index of where you wish to place your mark at your turn."
randomize()
play()


class TicTacToe {
  @board : Char[,];
  @cpu_opening : Bool;

  enum Status {
    INVALID_MOVE,
    PLAYING,
    QUIT,
    TIE,
    CPU_WIN,
    PLAYER_WIN
  }

  consts Weights {
    MIN := -1000,
    MAX := 1000
  }

  function : Main(args : String[]) ~ Nil {
    cpu_score := 0;
    player_score := 0;

    for(i :=0; i < 5; i += 1;) {
      game := TicTacToe->New();
      result := game->Play();

      if(result = Status->PLAYER_WIN) {
        player_score += 1;
        "\n=> Player Wins!"->PrintLine();
      }
      else if(result = Status->CPU_WIN) {
        cpu_score += 1;
        "\n=> CPU Wins!"->PrintLine();
      }
      else if(result = Status->TIE) {
        "\n=> Tie."->PrintLine();
      }
      else {
        break;
      };
    };

    "\nHuman={$player_score}, CPU={$cpu_score}"->PrintLine();
  }

  New() {
    @board := Char->New[3, 3];
    for(index := 0; index < 9; index += 1;) {
      j := index / 3;
      i := index % 3;
      @board[i, j] := '1' + index;
    };

    @cpu_opening := true;
  }

  method : Play() ~ Status {
    players_turn := Int->Random(1) = 1 ? true : false;

    if(players_turn) {
      @cpu_opening := false;
      "\n*** NEW (Player) ***\n"->PrintLine();
      Draw();
    }
    else {
      "\n*** NEW (CPU) ***\n"->PrintLine();
    };

    playing := true;
    do {
      status : Status;

      if(players_turn) {
        status := PlayerMove();
        players_turn := false;
      }
      else {
        status := CpuMove();
        players_turn := true;
      };

      if(players_turn) {
        Draw();
      };

      select(status) {
        label Status->INVALID_MOVE: {
          "\n=> Invalid Move"->PrintLine();
        }

        label Status->PLAYER_WIN: {
          return Status->PLAYER_WIN;
        }

        label Status->CPU_WIN: {
          return Status->CPU_WIN;
        }

        label Status->TIE: {
          return Status->TIE;
        }

        label Status->QUIT: {
          playing := false;
        }
      };
    }
    while(playing);

    return Status->QUIT;
  }

  method : PlayerMove() ~ Status {
    move := System.IO.Console->ReadString();
    if(move->Size() = 0) {
      return Status->INVALID_MOVE;
    };

    option := move->Get(0);
    if(option = 'q') {
      return Status->QUIT;
    };

    if(LegalMove(option, 'X')) {
      if(IsWinner(@board, 'X')) {
        return Status->PLAYER_WIN;
      }
      else if(IsTied()) {
        return Status->TIE;
      }
      else {
        return Status->PLAYING;
      };
    }
    else {
      return Status->INVALID_MOVE;
    };
  }

  method : CpuMove() ~ Status {
    if(@cpu_opening) {
      select(Int->Random(2)) {
        label 0: {
          @board[0, 0] := 'O';
        }

        label 1: {
          @board[1, 1] := 'O';
        }

        label 2: {
          @board[2, 2] := 'O';
        }
      };
      @cpu_opening := false;
    }
    else {
      BestCpuMove(CopyBoard());
    };

    if(IsWinner(@board, 'O')) {
      return Status->CPU_WIN;
    }
    else if(IsTied()) {
      return Status->TIE;
    }
    else {
      return Status->PLAYING;
    };
  }

  method : Minimax(board : Char[,], depth : Int, is_max : Bool, alpha : Int, beta : Int) ~ Int {
      score := EvaluateMove(board);
      if(score = 10 | score = -10) {
          return score;
      };

      if(IsTied()) {
        return 0;
      };

    if(is_max) {
          best := Weights->MIN;
          for(i := 0; i < 3; i += 1;) {
        for(j := 0; j < 3; j += 1;) {
          if(board[i,j] <> 'X' & board[i,j] <>'O') {
            test := board[i,j];
            board[i,j] := 'O';
            best := Int->Max(best, Minimax(board, depth + 1, false, alpha, beta));
            alpha := Int->Max(alpha, best);
            board[i,j] := test;

            if(beta <= alpha) {
              return best;
            };
          };
        };
      };

      return best;
    }
    else {
      best := Weights->MAX;
          for(i := 0; i < 3; i += 1;) {
        for(j := 0; j < 3; j += 1;) {
          if(board[i,j] <> 'X' & board[i,j] <>'O') {
            test := board[i,j];
            board[i,j] := 'X';
            best := Int->Min(best, Minimax(board, depth + 1, true, alpha, beta));
            beta := Int->Min(beta, best);
            board[i,j] := test;

            if(beta <= alpha) {
              return best;
            };
          };
        };
      };

      return best;
    };
    }

    method : BestCpuMove(board : Char[,]) ~ Nil {
      best := Weights->MIN; # empty
      best_i := -1;
      best_j := -1;

      for(i := 0; i < 3; i += 1;) {
      for(j := 0; j < 3; j += 1;) {
        if(board[i,j] <> 'X' & board[i,j] <> 'O') {
          test := board[i,j];
          board[i,j] := 'O';
          move := Int->Max(best, Minimax(board, 0, false, Weights->MIN, Weights->MAX));
          board[i,j] := test;

          if(move > best) {
            best_i := i;
              best_j := j;
            best := move;
          };
                };
      };
    };

    @board[best_i, best_j] := 'O';
    }

    method : EvaluateMove(board : Char[,]) ~ Int {
      if(IsWinner(board, 'O')) {
        return 10;
      }
      else if(IsWinner(board, 'X')) {
        return -10;
      }
      else {
        return 0;
      };
    }

    method : CopyBoard() ~ Char[,] {
      board := Char->New[3, 3];

      for(i := 0; i < 3; i += 1;) {
      for(j := 0; j < 3; j += 1;) {
        board[i,j] := @board[i,j];
      };
    };

    return board;
    }

  method : LegalMove(move : Char, player: Char) ~ Bool {
    if(move >= '1' & move <= '9') {
      index := (move - '1')->As(Int);
      j := index / 3; i := index % 3;

      if(@board[i, j] = 'X' | @board[i, j] = 'O') {
        return false;
      };

      @board[i, j] := player;
      return true;
    }
    else {
      return false;
    };
  }

  method : IsWinner(board : Char[,], player : Char) ~ Bool {
    # --- diagonal ---
    check := 0;
    for(i := 0; i < 3; i += 1;) {
      if(board[i, i] = player) {
        check += 1;
      };
    };

    if(check = 3) {
      return true;
    };

    check := 0;
    j := 2;
    for(i := 0; i < 3; i += 1;) {
      if(board[i, j] = player) {
        check += 1;
      };
      j -= 1;
    };

    if(check = 3) {
      return true;
    };

    # --- vertical ---
    for(i := 0; i < 3; i += 1;) {
      check := 0;
      for(j := 0; j < 3; j += 1;) {
        if(board[i, j] = player) {
          check += 1;
        };
      };

      if(check = 3) {
        return true;
      };
    };

    # --- horizontal ---
    for(j := 0; j < 3; j += 1;) {
      check := 0;
      for(i := 0; i < 3; i += 1;) {
        if(board[i, j] = player) {
          check += 1;
        };
      };

      if(check = 3) {
        return true;
      };
    };

    return false;
  }

  method : IsTied() ~ Bool {
    for(i := 0; i < 3; i += 1;) {
      for(j := 0; j < 3; j += 1;) {
        if(@board[i, j] <> 'X' & @board[i, j] <> 'O') {
          return false;
        };
      };
    };

    return true;
  }

  method : Draw() ~ Nil {
    a1 := @board[0, 0]; a2 := @board[1, 0]; a3 := @board[2, 0];
    b1 := @board[0, 1]; b2 := @board[1, 1]; b3 := @board[2, 1];
    c1 := @board[0, 2]; c2 := @board[1, 2]; c3 := @board[2, 2];

    "==========="->PrintLine();
    " {$a1} | {$a2} | {$a3} "->PrintLine();
    "---|---|---"->PrintLine();
    " {$b1} | {$b2} | {$b3} "->PrintLine();
    "---|---|---"->PrintLine();
    " {$c1} | {$c2} | {$c3} "->PrintLine();
    "===========\n"->PrintLine();
  }
}


program tic(Input, Output);

type
    Contents = (Unassigned, Human, Computer);
var
    best_i, best_j: Integer; { best solution a depth of zero in the search }
    b: array[0..2, 0..2] of Contents;  {zero based so modulus works later}
    player: Contents;

procedure displayBoard;
var
    i, j: Integer;
    t: array [Contents] of char;
begin
    t[Unassigned] := ' ';
    t[Human] := 'O';
    t[Computer] := 'X';
    for i := 0 to 2 do begin
        for j := 0 to 2 do begin
            write(t[b[i][j]]);
            if j <> 2 then write(' | ');
        end;
        writeln();
    if i < 2 then writeln('---------');
    end;
    writeln();
    writeln();
end;

function swapPlayer (Player: Contents): Contents;
begin
    if Player = Computer then swapPlayer := Human else swapPlayer := Computer;
end;

function checkWinner: Contents;
var
    i: Integer;
begin
    checkWinner := Unassigned;  (* no winner yet *)
    for i := 0 to 2 do begin
        (* first horizontal solution *)
        if ((checkWinner = Unassigned) and (b[i][0] <> Unassigned)) and ((b[i][1] = b[i][0]) and (b[i][2] = b[i][0])) then
            checkWinner := b[i][0]
        else
        (* now vertical solution *)
        if ((checkWinner = Unassigned) and (b[0][i] <> Unassigned)) and ((b[1][i] = b[0][i]) and (b[2][i] = b[0][i])) then
            checkWinner := b[0][i];
    end; (* now check the paths of the two cross line slants that share the middle position *)
    if (checkWinner = Unassigned) and (b[1][1] <> Unassigned) then begin
       if ((b[1][1] = b[0][0]) and (b[2][2] = b[0][0])) then checkWinner := b[0][0]
       else if ((b[1][1] = b[2][0]) and (b[0][2] = b[1][1])) then checkWinner := b[1][1];
    end;
end;

{ Basic strategy test - is this te best solution we have seen }
function saveBest(CurScore, CurBest: Contents) : boolean;
begin
    if CurScore = CurBest then saveBest := false
    else if (Curscore = Unassigned) and (CurBest = Human) then saveBest := false
    else if (Curscore = Computer) and ((CurBest = Unassigned) or (CurBest = Human)) then saveBest := false
    else saveBest := true;
end;

{ Basic strategy - recursive depth first serach of possible moves
  if computer can win save it, otherwise block if need be, else do deeper.
  At each level modify the board for the next call, but clean up as go back up,
  by remembering the modified position on the call stack. }
function test_move (val: Contents; depth: Integer): Contents;
var
    i, j: Integer;
    score, best, changed: Contents;
begin
    best := Computer;
    changed := Unassigned;
    score := checkWinner();
    if score <> Unassigned then begin
      if score = val then test_move := Human else test_move := Computer;
    end else begin
      for i := 0 to 2 do for j := 0 to 2 do begin
        if b[i][j] = Unassigned then begin
          changed := val;
          b[i][j] := val; (* the value for now and try wioth the other player *)
          score := test_move(swapPlayer(val), depth + 1);
          if (score <> Unassigned) then score := swapPlayer(score);
          b[i][j] := Unassigned;
          if saveBest(score, best) then begin
            if depth = 0 then begin { top level, so remember actual position }
              best_i := i;
              best_j := j;
            end;
            best := score;
          end;
        end;
      end;
      if (changed <> Unassigned) then test_move := best else test_move := Unassigned;
    end;
end;

function playGame(whom:Contents): string;
var
    i, j, k, move: Integer;
    win: Contents;
begin
    win := Unassigned;
    for i := 0 to 2 do for j := 0 to 2 do b[i][j] := Unassigned;

    writeln('The board positions are numbered as follows:');
    writeln('1 | 2 | 3');
    writeln('---------');
    writeln('4 | 5 | 6');
    writeln('---------');
    writeln('7 | 8 | 9');
    writeln('You have O, I have X.');
    writeln();

    k := 1;
	repeat {rather a for loop but can not have two actions or early termination in Pascal}
        begin
          if whom = Human then begin
            repeat begin
              write('Your move: ');
              readln(move);
              if (move < 1) or (move > 9) then writeln('Opps: enter a number between 1 - 9')
              else move := pred(move); {humans do 1 -9, but the computer wants 0-8 for modulus to work}
            end until ((move >= 0) and (move <= 8));
            i := move div 3; { convert from range to corridinated of the array }
            j := move mod 3;
            if b[i][j] = Unassigned then b[i][j] := Human;
          end;
          if  whom = Computer then begin
            (* randomize if computer opens, so its not always the same game *)
            if k = 1 then begin
              best_i := random(3); {Pascal random returns positive integers from 0 to func arg}
              best_j := random(3);
            end else
              win := test_move(Computer, 0);
            b[best_i][best_j] := Computer;
            writeln('My move: ', best_i * 3 + best_j + 1);
          end;

          displayBoard();
          win := checkWinner();
          if win <> Unassigned then begin
            if win = Human then playGame := 'You win.' else playGame := 'I win.';
          end else begin
			k := succ(k); { "for" loop counter actions }
            whom := swapPlayer(whom);
          end;
        end;
	until (win <> Unassigned) or (k > 9);
	if win = Unassigned then playGame := 'A draw.';
end;

begin
    randomize();
    player := Human;
    while (true) do
        begin
            writeln(playGame(player));
            writeln();
            player := swapPlayer(player);
        end
end.


use warnings;
use strict;

my $initial = join ",", qw(abc def ghi);
my %reverse = qw(X O O X);

# In list context, returns best move,
# In scalar context, returns the score of best move.
my %cache;
sub best_move {
	my ($b, $me) = @_;
	if( exists $cache{$b,$me,wantarray} ) {
		return $cache{$b,$me,wantarray};
	} elsif( my $s = score( $b, $me ) ) {
		return $cache{$b,$me,wantarray} = (wantarray ? undef : $s);
	}
	my $him = $reverse{$me};
	my ($best, @best) = (-999);
	for my $m (moves($b)) {
		(my $with_m = $b) =~ s/$m/$me/ or die;
		# The || operator supplies scalar context to best_move(...)
		my $s = -(score($with_m, $him) || best_move($with_m, $him));
		if( $s > $best ) {
			($best, @best) = ($s, $m);
		} elsif( $s == $best ) {
			push @best, $m;
		}
	}
	$cache{$b,$me,wantarray} = wantarray ? $best[rand @best] : $best;
}

my $winner = q[([XOxo])(?:\1\1|...\1...\1|..\1..\1|....\1....\1)];
sub score {
	my ($b, $me) = @_;
	$b =~ m/$winner/o or return 0;
	return $1 eq $me ? +1 : -1;
}

sub moves {
	my ($b) = @_;
	$b =~ /([^xoXO,\n])/g;
}

sub print_board {
	my ($b) = @_;
	$b =~ s/\B/|/g;
	$b =~ s/,/\n-+-+-\n/g;
	print $b, "\n";
}

sub prompt {
	my ($b, $color) = @_;
	my @moves = moves($b);
	unless( @moves ) {
		return;
	}
	while( 1 ) {
		print "Place your $color on one of [@moves]: ";
		defined(my $m = <>) or return;
		chomp($m);
		return $m if grep $m eq $_, @moves;
	}
}

my @players = (
	{ whose => "your", name => "You",
	  verb => "You place", get_move => \&prompt },
	{ whose => "the computer's", name => "Computer",
	  verb => "The computer places", get_move => \&best_move },
);
my $whose_turn = int rand 2;

my $color = "X";
my $b = $initial;

while( 1 ) {
	my $p = $players[$whose_turn];
	print_board($b);
	print "It is $p->{whose} turn.\n";
	# The parens around $m supply list context to the right side
	# or the = operator, which causes sub best_move to return the
	# best move, rather than the score of the best move.
	my ( $m ) = $p->{get_move}->($b, $color);
	if( $m ) {
		print "$p->{verb} an $color at $m\n";
		$b =~ s/$m/$color/;
		my $s = score($b, $color) or next;
		print_board($b);
		print "$p->{name} ", $s > 0 ? "won!\n" : "lost!\n";
	} else {
		print "$p->{name} cannot move.\n";
	}
	print "Game over.\nNew Game...\n";
	($b, $color, $whose_turn) = ($initial, "X", int rand 2);
	redo;
} continue {
	$color = $reverse{$color};
	$whose_turn = !$whose_turn;
}


--
-- demo\rosetta\Tic_tac_toe.exw
--
with javascript_semantics
include pGUI.e
constant title = "Tic Tac Toe"
sequence board = repeat(' ',9)  -- {' '/'X'/'O'}
bool human = false  -- (flipped in new_game)
bool game_over = false
constant play_dumb = false
Ihandle dlg

-- saved in redraw_cb() for check_position():
integer cw,ch,  -- board centre
        d,      -- tile spacing
        h       -- tile size/radius

function redraw_cb(Ihandle ih, integer /*posx*/, /*posy*/)
    {cw,ch} = IupGetIntInt(ih, "DRAWSIZE")
    d = floor(min(cw,ch)/8)
    h = floor(d*2/3)
    cw = floor(cw/2)
    ch = floor(ch/2)

    IupGLMakeCurrent(ih)
    cdCanvas cddbuffer = IupGetAttributePtr(ih,"DBUFFER")
    cdCanvasActivate(cddbuffer)
    cdCanvasClear(cddbuffer)
    cdCanvasSetForeground(cddbuffer,CD_BLUE)
    cdCanvasSetLineWidth(cddbuffer,10)
    integer d3 = 3*d
    cdCanvasLine(cddbuffer,cw-d,ch-d3,cw-d,ch+d3)
    cdCanvasLine(cddbuffer,cw+d,ch-d3,cw+d,ch+d3)
    cdCanvasLine(cddbuffer,cw-d3,ch-d,cw+d3,ch-d)
    cdCanvasLine(cddbuffer,cw-d3,ch+d,cw+d3,ch+d)
    integer pdx = 1
    for y=+1 to -1 by -1 do
        integer my = ch+y*2*d
        for x=-1 to +1 do
            integer mx = cw+x*2*d
            integer mark = board[pdx]
            if mark='X' then
                cdCanvasLine(cddbuffer,mx-h,my-h,mx+h,my+h)
                cdCanvasLine(cddbuffer,mx-h,my+h,mx+h,my-h)
            elsif mark='O' then
                cdCanvasCircle(cddbuffer,mx,my,2*h)
            end if
            pdx += 1
        end for
    end for
    cdCanvasFlush(cddbuffer)
    return IUP_DEFAULT
end function

function map_cb(Ihandle canvas)
    IupGLMakeCurrent(canvas)
    cdCanvas cddbuffer
    if platform()=JS then
        cddbuffer = cdCreateCanvas(CD_IUP, canvas)
    else
        atom res = IupGetDouble(NULL, "SCREENDPI")/25.4
        cddbuffer = cdCreateCanvas(CD_GL, "10x10 %g", {res})
    end if
    IupSetAttributePtr(canvas,"DBUFFER",cddbuffer)
    cdCanvasSetBackground(cddbuffer, CD_PARCHMENT)
    return IUP_DEFAULT
end function

function canvas_resize_cb(Ihandle canvas)
    cdCanvas cddbuffer = IupGetAttributePtr(canvas,"DBUFFER")
    integer {canvas_width, canvas_height} = IupGetIntInt(canvas, "DRAWSIZE")
    atom res = IupGetDouble(NULL, "SCREENDPI")/25.4
    cdCanvasSetAttribute(cddbuffer, "SIZE", "%dx%d %g", {canvas_width, canvas_height, res})
    return IUP_DEFAULT
end function

constant wins = {{1,2,3},{4,5,6},{7,8,9},{1,4,7},{2,5,8},{3,6,9},{1,5,9},{3,5,7}}

function check_winner()
    for w=1 to length(wins) do
        integer {i,j,k} = wins[w],
                  mark = board[i]
        if mark!=' ' and mark=board[j] and mark=board[k] then
            return mark
        end if
    end for
    return 0
end function

integer best_i

function test_move(integer mark, depth)
    integer score = check_winner(),
            best = -1, changed = 0
    if score!=0 then return iff(score=mark?1:-1) end if
    for i=1 to 9 do
        if board[i]=' ' then
            changed = mark
            board[i] = mark
            score = -test_move('O'+'X'-mark, depth + 1)
            board[i] = ' '
            if score>best then
                if depth=0 then
                    best_i = i;
                end if
                best = score;
            end if
        end if
    end for
    return iff(changed!=0?best:0)
end function

procedure check_game_over()
    integer win = check_winner()
    if win or not find(' ',board) then
        string winner = iff(win='O'?"You win!",
                        iff(win='X'?"Computer wins"
                                   :"Draw"))
        IupSetStrAttribute(dlg,"TITLE","%s - %s",{title,winner})
        game_over = true
    end if
end procedure

procedure play_move(integer move)
    if move then
        assert(board[move]=' ')
        board[move] = 'O'
        check_game_over()
        if not game_over then
            human = false
            if play_dumb then
                sequence s = find_all(' ',board)
                best_i = s[rand(length(s))]
            else
                {} = test_move('X', 0)
                assert(board[best_i]=' ')
            end if
            board[best_i] = 'X'
            check_game_over()
            human = not game_over
        end if
    end if
end procedure

procedure new_game()
    board = repeat(' ',9)
    human = not human
    if not human then
        board[rand(9)] = 'X'
        human = true
    end if
end procedure

function check_position(integer px, py)
--
-- check if x,y is on a legal move.
-- uses ch,cw,d,h as saved by redraw_cb().
--
    integer pdx = 1
    for y=-1 to +1 do
        integer my = ch+y*2*d
        for x=-1 to +1 do
            integer mx = cw+x*2*d
            if  px>=mx-h and px<=mx+h
            and py>=my-h and py<=my+h then
                integer mark = board[pdx]
                return iff(mark==' '?pdx:0)
            end if
            pdx += 1
        end for
    end for
    return 0
end function

function button_cb(Ihandle /*canvas*/, integer button, pressed, x, y, atom /*pStatus*/)
    if button=IUP_BUTTON1 and not pressed then      -- (left button released)
        if game_over then
            game_over = false
            new_game()
        else
            play_move(check_position(x,y))
        end if
        IupRedraw(dlg)
    end if
    return IUP_CONTINUE
end function

function exit_cb(Ihandle /*ih*/)
    return IUP_CLOSE
end function

constant help_text = """
Tic Tac Toe, also known as Noughts and Crosses.

The aim is to get three Os (or Xs) in a row.

Human(O) plays first, as does loser. After a draw first player alternates.
Computer(X) plays a random move first, to make it more interesting.
Setting the constant play_dumb to true disables the internal AI.

Once a game is over click anywhere on the board to start a new game.
"""

function help_cb(Ihandln /*ih*/)
    IupMessage(title,help_text)
    return IUP_DEFAULT
end function

-- Other possible keys:
-- Q - quit (end program) [==X?]
-- C - concede (start new game)
function key_cb(Ihandle /*dlg*/, atom c)
    if c=K_ESC then return IUP_CLOSE end if
    if c=K_F1 then return help_cb(NULL) end if
    return IUP_CONTINUE
end function

procedure main()
    IupOpen()
    Ihandle canvas = IupGLCanvas("RASTERSIZE=800x800")
    dlg = IupDialog(canvas,`TITLE="%s",MINSIZE=245x180`,{title})
    IupSetCallbacks(canvas,{"MAP_CB",Icallback("map_cb"),
                            "ACTION",Icallback("redraw_cb"),
                            "RESIZE_CB", Icallback("canvas_resize_cb"),
                            "BUTTON_CB", Icallback("button_cb")})
    IupSetCallback(dlg, "KEY_CB", Icallback("key_cb"))
    IupSetAttributeHandle(NULL,"PARENTDIALOG",dlg)
    new_game()
    IupShow(dlg)
    IupSetAttribute(canvas,"RASTERSIZE",NULL)
    if platform()!=JS then
        IupMainLoop()
        IupClose()
    end if
end procedure

main()


<?php
const BOARD_NUM = 9;
const ROW_NUM = 3;
$EMPTY_BOARD_STR = str_repeat('.', BOARD_NUM);

function isGameOver($board, $pin) {
	$pat =
		'/X{3}|' . //Horz
		'X..X..X..|' . //Vert Left
		'.X..X..X.|' . //Vert Middle
		'..X..X..X|' . //Vert Right
		'..X.X.X..|' . //Diag TL->BR
		'X...X...X|' . //Diag TR->BL
		'[^\.]{9}/i'; //Cat's game
	if ($pin == 'O') $pat = str_replace('X', 'O', $pat);
	return preg_match($pat, $board);
}

//Start
$boardStr = isset($_GET['b'])? $_GET['b'] : $EMPTY_BOARD_STR;
$turn = substr_count($boardStr, '.')%2==0? 'O' : 'X';
$oppTurn = $turn == 'X'? 'O' : 'X';
$gameOver = isGameOver($boardStr, $oppTurn);

//Display board
echo '';
echo '
';
$p = 0;
for ($r = 0; $r < ROW_NUM; $r++) {
	echo '
';
	for ($c = 0; $c < ROW_NUM; $c++) {
		$pin = $boardStr[$p];

		echo '';
		if ($gameOver || $pin != '.') echo '', $pin, ''; //Occupied
		else { //Available
			$boardDelta = $boardStr;
			$boardDelta[$p] = $turn;
			echo '';
			echo $boardStr[$p];
			echo '';
		}

		echo '	';
		$p++;
	}
	echo '
';
	echo '<input type="hidden" name="b" value="', $boardStr, '"/>';
}
echo '
';
echo 'Reset';
if ($gameOver) echo '
Game Over!
';


(load "@lib/simul.l")  # for 'game' function

(de display ()
   (for Y (3 2 1)
      (prinl "   +---+---+---+")
      (prin " " Y)
      (for X (1 2 3)
         (prin " | " (or (get *Board X Y) " ")) )
      (prinl " |") )
   (prinl "   +---+---+---+")
   (prinl "     a   b   c") )

(de find3 (P)
   (find
      '((X Y DX DY)
         (do 3
            (NIL (= P (get *Board X Y)))
            (inc 'X DX)
            (inc 'Y DY)
            T ) )
      (1 1 1 1 2 3 1 1)
      (1 2 3 1 1 1 1 3)
      (1 1 1 0 0 0 1 1)
      (0 0 0 1 1 1 1 -1) ) )

(de myMove ()
   (when
      (game NIL 8
         '((Flg)     # Moves
            (unless (find3 (or (not Flg) 0))
               (make
                  (for (X . L) *Board
                     (for (Y . P) L
                        (unless P
                           (link
                              (cons
                                 (cons X Y (or Flg 0))
                                 (list X Y) ) ) ) ) ) ) ) )
         '((Mov) # Move
            (set (nth *Board (car Mov) (cadr Mov)) (cddr Mov)) )
         '((Flg)     # Cost
            (if (find3 (or Flg 0)) -100 0) ) )
      (let Mov (caadr @)
         (set (nth *Board (car Mov) (cadr Mov)) 0) )
      (display) ) )

(de yourMove (X Y)
   (and
      (sym? X)
      (>= 3 (setq X (- (char X) 96)) 1)
      (num? Y)
      (>= 3 Y 1)
      (not (get *Board X Y))
      (set (nth *Board X Y) T)
      (display) ) )

(de main ()
   (setq *Board (make (do 3 (link (need 3)))))
   (display) )

(de go Args
   (cond
      ((not (yourMove (car Args) (cadr Args)))
         "Illegal move!" )
      ((find3 T) "Congratulation, you won!")
      ((not (myMove)) "No moves")
      ((find3 0) "Sorry, you lost!") ) )


:- use_module('min-max.pl').

:-dynamic box/2.
:- dynamic tic_tac_toe_window/1.

% Computer begins.
tic-tac-toe(computer) :-
	V is random(9),
	TTT = [_,_,_,_,_,_ ,_,_,_],
	nth0(V, TTT, o),
	display_tic_tac_toe(TTT).

% Player begins
tic-tac-toe(me) :-
	TTT = [_,_,_,_,_,_ ,_,_,_],
	display_tic_tac_toe(TTT).

display_tic_tac_toe(TTT) :-
	retractall(box(_,_)),
	retractall(tic_tac_toe_window(_)),
	new(D, window('Tic-tac-Toe')),
	send(D, size, size(170,170)),
	X = 10, Y = 10,
	display(D, X, Y, 0, TTT),
	assert(tic_tac_toe_window(D)),
	send(D, open).

display(_, _, _, _, []).

display(D, X, Y, N, [A,B,C|R]) :-
	display_line(D, X, Y, N, [A,B,C]),
	Y1 is Y+50,
	N3 is N+3,
	display(D, X, Y1, N3, R).

display_line(_, _, _, _, []).
display_line(D, X, Y, N, [C|R]) :-
	(   nonvar(C)-> C1 = C; C1 = ' '),
	new(B, tic_tac_toe_box(C1)),
	assertz(box(N, B)),
	send(D, display, B, point(X, Y)),
	X1 is X + 50,
	N1 is N+1,
	display_line(D, X1, Y, N1, R).

% class tic_tac_toe_box
% display an 'x' when the player clicks
% display an 'o' when the computer plays
:- pce_begin_class(tic_tac_toe_box, box, "Graphical window with text").

variable(mess, any, both, "text to display").

initialise(P, Lbl) :->
	send(P, send_super, initialise),
	send(P, slot, mess, Lbl),
	WS = 50, HS = 50,
	send(P, size, size(WS,HS)),
	send(P, recogniser,
	     handler_group(new(click_gesture(left,
					     '',
					     single,
					     message(@receiver, my_click))))).

% the box is clicked
my_click(B) :->
	send(B, set_val, x),
	send(@prolog, play).

% only works when the box is "free"
set_val(B, Val) :->
	get(B, slot, mess, ' '),
	send(B, slot, mess, Val),
	send(B, redraw),
	send(B, flush).

%  redefined method to display custom graphical objects.
'_redraw_area'(P, A:area) :->
	send(P, send_super, '_redraw_area', A),
	%we display the text
	get(P, slot, mess, Lbl),
	new(Str1, string(Lbl)),
	get_object(P, area, area(X,Y,W,H)),
	send(P, draw_box, X, Y, W, H),
	send(P, draw_text, Str1,
		font(times, normal, 30),
		X, Y, W, H, center, center).

:- pce_end_class.

play :-
	numlist(0, 8, L),
	maplist(init, L, TTT),
	finished(x, TTT, Val),
	(   Val = 2 -> send(@display, inform,'You win !'),
	               tic_tac_toe_window(D),
		       send(D, destroy)
	;   (	Val = 1 -> send(@display, inform,'Draw !'),
	                   tic_tac_toe_window(D),
		           send(D, destroy)
	    ;	next_move(TTT, TT1),
		maplist(display, L, TT1),
		finished(o, TT1, V),
		(   V = 2 -> send(@display, inform,'I win !'),
			     tic_tac_toe_window(D),
		             send(D, destroy)
		;   (	V = 1 -> send(@display, inform,'Draw !'),
		                 tic_tac_toe_window(D),
			         send(D, destroy)
		    ;	true)))).

% use minmax to compute the next move
next_move(TTT, TT1) :-
	minimax(o, 0, 1024, TTT, _V1- TT1).

% we display the new board
display(I, V) :-
	nonvar(V),
	box(I, V1),
	send(V1, set_val, V).

display(_I, _V).

% we create the board for minmax
init(I, V) :-
	box(I, V1),
	get(V1, slot, mess, V),
	V \= ' '.

init(_I, _V).

% winning position for the player P ?
winned(P, [A1, A2, A3, A4, A5, A6, A7, A8, A9]) :-
       (is_winning_line(P, [A1, A2, A3]);
	is_winning_line(P, [A4, A5, A6]);
	is_winning_line(P, [A7, A8, A9]);
	is_winning_line(P, [A1, A4, A7]);
	is_winning_line(P, [A2 ,A5, A8]);
	is_winning_line(P, [A3, A6, A9]);
	is_winning_line(P, [A1, A5, A9]);
	is_winning_line(P, [A3, A5, A7])).

is_winning_line(P, [A, B, C]) :-
	nonvar(A), A = P,
	nonvar(B), B = P,
	nonvar(C), C = P.

% Winning position for the player
eval(Player, Deep, TTT, V) :-
	winned(Player, TTT),
	(   Player = o -> V is 1000 - 50 * Deep; V is -1000+ 50 * Deep).

% Loosing position for the player
eval(Player, Deep, TTT, V) :-
	select(Player, [o,x], [Player1]),
	winned(Player1, TTT),
	(   Player = x -> V is 1000 - 50 * Deep; V is -1000+ 50 * Deep).

% Draw position
eval(_Player, _Deep, TTT, 0) :-
	include(var, TTT, []).

% we fetch the free positions of the board
possible_move(TTT, LMove) :-
	new(C, chain),
	forall(between(0,8, I),
	       (   nth0(I, TTT, X),
		   (   var(X) -> send(C, append, I); true))),
	chain_list(C, LMove).

% we create the new position when the player P clicks
% the box "N"
assign_move(P, TTT, N, TT1) :-
	copy_term(TTT, TT1),
	nth0(N, TT1, P).

% We fetch all the possible boards obtained from board TTT
% for the player P
get_next(Player, Deep, TTT, Player1, Deep1, L):-
	possible_move(TTT, LMove),
	select(Player, [o,x], [Player1]),
	Deep1 is Deep + 1,
	maplist(assign_move(Player, TTT), LMove, L).

% The game is over ?
% Player P wins
finished(P, TTT, 2) :-
	winned(P, TTT).

% Draw
finished(_P, TTT, 1) :-
	include(var, TTT, []).

% the game is not over
finished(_P, _TTT, 0) .

% minmax must knows when the computer plays
% (o for ordinateur in French)
computer(o).


'''
    Tic-tac-toe game player.
    Input the index of where you wish to place your mark at your turn.
'''

import random

board = list('123456789')
wins = ((0,1,2), (3,4,5), (6,7,8),
        (0,3,6), (1,4,7), (2,5,8),
        (0,4,8), (2,4,6))

def printboard():
    print('\n'.join(' '.join(board[x:x+3]) for x in(0,3,6)))

def score():
    for w in wins:
        b = board[w[0]]
        if b in 'XO' and all (board[i] == b for i in w):
            return b, [i+1 for i in w]
    return None, None

def finished():
    return all (b in 'XO' for b in board)

def space():
    return [ b for b in board if b not in 'XO']

def my_turn(xo):
    options = space()
    choice = random.choice(options)
    board[int(choice)-1] = xo
    return choice

def your_turn(xo):
    options = space()
    while True:
        choice = input(" Put your %s in any of these positions: %s "
                       % (xo, ''.join(options))).strip()
        if choice in options:
            break
        print( "Whoops I don't understand the input" )
    board[int(choice)-1] = xo
    return choice

def me(xo='X'):
    printboard()
    print('I go at', my_turn(xo))
    return score()
    assert not s[0], "\n%s wins across %s" % s

def you(xo='O'):
    printboard()
    # Call my_turn(xo) below for it to play itself
    print('You went at', your_turn(xo))
    return score()
    assert not s[0], "\n%s wins across %s" % s

print(__doc__)
while not finished():
    s = me('X')
    if s[0]:
        printboard()
        print("\n%s wins across %s" % s)
        break
    if not finished():
        s = you('O')
        if s[0]:
            printboard()
            print("\n%s wins across %s" % s)
            break
else:
    print('\nA draw')


rm(list=ls())
library(RColorBrewer)

# Create tic.tac.toe function.

tic.tac.toe <- function(name="Name", mode=0, type=0){

  place.na <<- matrix(1:9, 3, 3)
  value <<- matrix(-3, 3, 3)
  k <<- 1 ; r <<- 0

  # Make game board.

  image(1:3, 1:3, matrix(sample(9), 3, 3), asp=c(1, 1),
        xaxt="n", yaxt="n", xlab="", ylab="", frame=F, col=brewer.pal(9, "Set3"))
  segments(c(0.5,0.5,1.5,2.5), c(2.5,1.5,0.5,0.5),
           c(3.5,3.5,1.5,2.5), c(2.5,1.5,3.5,3.5), lwd=8, col=gray(0.3))
  segments(c(0.5,0.5,0.5,3.5), c(0.52,3.47,0.5,0.5),
           c(3.5,3.5,0.5,3.5), c(0.52,3.47,3.5,3.5), lwd=8, col=gray(0.3))

  # Allow player to choose between a human v. human, human v. random AI, or human vs. smart AI.

  if(mode==0) title(list(paste(name, "'s Tic-Tac-Toe !"), cex=2),
                    "2P : Human v.s. Human", font.sub=2, cex.sub=2)
  if(mode==1) title(list(paste(name, "'s Tic-Tac-Toe !"), cex=2),
                    "1P : Human v.s. AI (Easy)", font.sub=2, cex.sub=2)
  if(mode==2) title(list(paste(name, "'s Tic-Tac-Toe !"), cex=2),
                    "1P : Human v.s. AI (Hard)", font.sub=2, cex.sub=2)

  # Dole out symbols.

  if(type==0){symbol <- "O" ; symbol.op <- "X"}
  if(type==1){symbol <- "X" ; symbol.op <- "O"}
  out <- list(name=name, mode=mode, type=type, symbol=symbol, symbol.op=symbol.op)
}

# Checks if the game has ended.

isGameOver <- function(){

  for(i in 1:3){
    total.1 <- 0 ; total.2 <- 0
    for(j in 1:3){
      total.1 <- total.1 + value[i, j]
      total.2 <- total.2 + value[j, i]
    }
    if(total.1==0 | total.2==0 | total.1==3 | total.2==3){
      break
    }
  }
  total.3 <- value[1, 1] + value[2, 2] + value[3, 3]
  total.4 <- value[1, 3] + value[2, 2] + value[3, 1]

  if(total.1==0 | total.2==0 | total.3==0 | total.4==0 | total.1==3 | total.2==3 | total.3==3 | total.4==3){
    place.na[!is.na(place.na)] <<- NA
    if(total.1==0 | total.2==0 | total.3==0 | total.4==0){
      title(sub=list(""You Won ?! That's a first!", col="red", font=2, cex=2.5), line=2)
    }else{
      title(sub=list("You Don't Get Tired of Losing ?!", col="darkblue", font=2, cex=2.5), line=2)
    }
  }

  if(all(is.na(place.na))){
    if(total.1==0 | total.2==0 | total.3==0 | total.4==0 | total.1==3 | total.2==3 | total.3==3 | total.4==3){
      if(total.1==0 | total.2==0 | total.3==0 | total.4==0){
        title(sub=list("You Won ! Pigs Must Be Flying!", col="orange", font=2, cex=2.5), line=2)
      }else{
        title(sub=list("You Lost ... Once Again !", col="darkblue", font=2, cex=2.5), line=2)
      }
    }else{
      title(sub=list("A measly tie! Try Again", col="blue", font=2, cex=2.5), line=2)
    }
  }
}

# AI attack function

attack <- function(){
  ### Identify rows and columns
  for(i in 1:3){
    total.1 <- 0 ; total.2 <- 0
    for(j in 1:3){
      total.1 <- total.1 + value[i, j]
      total.2 <- total.2 + value[j, i]
    }
    if(total.1==-1 | total.2==-1){
      break
    }
  }
  total.3 <- value[1, 1] + value[2, 2] + value[3, 3]
  total.4 <- value[1, 3] + value[2, 2] + value[3, 1]

  if(total.1==-1){
    text(i, which(value[i,]!=1), symbol.op, cex=6, font=2)
    place.na[i, which(value[i,]!=1)] <<- NA
    value[i, which(value[i,]!=1)] <<- 1
  }else if(total.2==-1){
    text(which(value[,i]!=1), i, symbol.op, cex=6, font=2)
    place.na[which(value[,i]!=1), i] <<- NA
    value[which(value[,i]!=1), i] <<- 1
  }else if(total.3==-1){
    r.1 <- which(c(value[1, 1], value[2, 2], value[3, 3])!=1)
    text(r.1, r.1, symbol.op, cex=6, font=2)
    place.na[r.1, r.1] <<- NA
    value[r.1, r.1] <<- 1
  }else if(total.4==-1){
    r.2 <- which(c(value[1, 3], value[2, 2], value[3, 1])!=1)
    text(r.2, -r.2+4, symbol.op, cex=6, font=2)
    place.na[r.2, -r.2+4] <<- NA
    value[r.2, -r.2+4] <<- 1
  }
}

# AI defense function

defend <- function(){

  for(i in 1:3){
    total.1 <- 0 ; total.2 <- 0
    for(j in 1:3){
      total.1 <- total.1 + value[i, j]
      total.2 <- total.2 + value[j, i]
    }
    if(total.1==-3 | total.2==-3){
      break
    }
  }
  total.3 <- value[1, 1] + value[2, 2] + value[3, 3]
  total.4 <- value[1, 3] + value[2, 2] + value[3, 1]

  if(total.1==-3){
    text(i, which(value[i,]!=0), symbol.op, cex=6, font=2)
    place.na[i, which(value[i,]!=0)] <<- NA
    value[i, which(value[i,]!=0)] <<- 1
  }else if(total.2==-3){
    text(which(value[,i]!=0), i, symbol.op, cex=6, font=2)
    place.na[which(value[,i]!=0), i] <<- NA
    value[which(value[,i]!=0), i] <<- 1
  }else if(total.3==-3){
    r.1 <- which(c(value[1, 1], value[2, 2], value[3, 3])!=0)
    text(r.1, r.1, symbol.op, cex=6, font=2)
    place.na[r.1, r.1] <<- NA
    value[r.1, r.1] <<- 1
  }else if(total.4==-3){
    r.2 <- which(c(value[1, 3], value[2, 2], value[3, 1])!=0)
    text(r.2, -r.2+4, symbol.op, cex=6, font=2)
    place.na[r.2, -r.2+4] <<- NA
    value[r.2, -r.2+4] <<- 1
  }else{
    rn <- sample(place.na[!is.na(place.na)], 1)
    text(rn-3*rn%/%3.5, rn%/%3.5+1, symbol.op, cex=6, font=2)
    place.na[rn-3*rn%/%3.5, rn%/%3.5+1] <<- NA
    value[rn-3*rn%/%3.5, rn%/%3.5+1] <<- 1
  }
}

# Allow aim in program.

aim <- function(x, y, tic.tac.toe=ttt){

  mode <- tic.tac.toe$mode
  symbol <<- tic.tac.toe$symbol
  symbol.op <<- tic.tac.toe$symbol.op
  x <<- x ; y <<- y

  # Mode 0, Two Players

  if(mode==0){
    turn <- rep(c(0, 1), length.out=9)
    if(is.na(place.na[x, y])){
      cat("This square is taken !")
    }else{

      if(turn[k]==0){
        text(x, y, symbol, cex=6, font=2)
        place.na[x, y] <<- NA
        value[x, y] <<- 0
      }
      if(turn[k]==1){
        text(x, y, symbol.op, cex=6, font=2)
        place.na[x, y] <<- NA
        value[x, y] <<- 1
      }
      k <<- k + 1
    }
  }

  # Mode 1, Random AI

  if(mode==1){
    if(is.na(place.na[x, y])){
      cat("This square had been chosen !")
    }else{
      text(x, y, symbol, cex=6, font=2)
      place.na[x, y] <<- NA
      value[x, y] <<- 0
      isGameOver()

      for(i in 1:3){
        total.1 <- 0 ; total.2 <- 0
        for(j in 1:3){
          total.1 <- total.1 + value[i, j]
          total.2 <- total.2 + value[j, i]
        }
        if(total.1==-1 | total.2==-1){
          break
        }
      }
      total.3 <- value[1, 1] + value[2, 2] + value[3, 3]
      total.4 <- value[1, 3] + value[2, 2] + value[3, 1]

      if(all(is.na(place.na))){
        isGameOver()
      }else if(total.1==-1 | total.2==-1 | total.3==-1 | total.4==-1){
        attack()
      }else{
        defend()
      }
    }
  }

  # Mode 2, Hard AI

  if(mode==2){

    if(is.na(place.na[x, y])){
      cat("This square is taken!")
    }else{

      # AI First Turn

      if(sum(is.na(place.na))==0){

        text(x, y, symbol, cex=6, font=2)
        place.na[x, y] <<- NA
        value[x, y] <<- 0

        if(is.na(place.na[2, 2])==F){
          text(2, 2, symbol.op, cex=6, font=2)
          place.na[2, 2] <<- NA
          value[2, 2] <<- 1
        }else{
          corner.1 <- sample(c(1, 3), 1) ; corner.2 <- sample(c(1, 3), 1)
          text(corner.1, corner.2, symbol.op, cex=6, font=2)
          place.na[corner.1, corner.2] <<- NA
          value[corner.1, corner.2] <<- 1
        }

        # AI Second Turn

      }else if(sum(is.na(place.na))==2){
        text(x, y, symbol, cex=6, font=2)
        place.na[x, y] <<- NA
        value[x, y] <<- 0

        for(i in 1:3){
          total.1 <- 0 ; total.2 <- 0
          for(j in 1:3){
            total.1 <- total.1 + value[i, j]
            total.2 <- total.2 + value[j, i]
          }
          if(total.1==-3 | total.2==-3){
            break
          }
        }
        total.3 <- value[1, 1] + value[2, 2] + value[3, 3]
        total.4 <- value[1, 3] + value[2, 2] + value[3, 1]

        if(total.1==-3 | total.2==-3 | total.3==-3 | total.4==-3){
          defend()
        }else{
          total.1 <- value[2, 1] + value[2, 2] + value[2, 3]
          total.2 <- value[1, 2] + value[2, 2] + value[3, 2]
          total.3 <- value[1, 1] + value[2, 2] + value[3, 3]
          total.4 <- value[1, 3] + value[2, 2] + value[3, 1]
          if(total.1==1 | total.2==1 | total.3==1 | total.4==1){
            if((value[2, 2]==1 & total.3==1) | (value[2, 2]==1 & total.4==1)){
              vector.side <- c(place.na[2, 1], place.na[1, 2], place.na[3, 2], place.na[2, 3])
              rn <- sample(vector.side[!is.na(vector.side)], 1)
              text(rn-3*rn%/%3.5, rn%/%3.5+1, symbol.op, cex=6, font=2)
              place.na[rn-3*rn%/%3.5, rn%/%3.5+1] <<- NA
              value[rn-3*rn%/%3.5, rn%/%3.5+1] <<- 1
            }else{
              matrix.cor <- place.na[c(1, 3), c(1, 3)]
              rn <- sample(matrix.cor[!is.na(matrix.cor)], 1)
              text(rn-3*rn%/%3.5, rn%/%3.5+1, symbol.op, cex=6, font=2)
              place.na[rn-3*rn%/%3.5, rn%/%3.5+1] <<- NA
              value[rn-3*rn%/%3.5, rn%/%3.5+1] <<- 1
            }
          }else{
            if((x==1 & y==2) | (x==3 & y==2)){
              rn <- sample(c(1, 3), 1)
              text(x, rn, symbol.op, cex=6, font=2)
              place.na[x, rn] <<- NA
              value[x, rn] <<- 1
            }else if((x==2 & y==3) | (x==2 & y==1)){
              rn <- sample(c(1, 3), 1)
              text(rn, y, symbol.op, cex=6, font=2)
              place.na[rn, y] <<- NA
              value[rn, y] <<- 1
            }else if((x==1 & y==1) | (x==1 & y==3) | (x==3 & y==1) | (x==3 & y==3)){
              text(-x+4, -y+4, symbol.op, cex=6, font=2)
              place.na[-x+4, -y+4] <<- NA
              value[-x+4, -y+4] <<- 1
            }
          }
        }

        # AI Other Turn

      }else{
        text(x, y, symbol, cex=6, font=2)
        place.na[x, y] <<- NA
        value[x, y] <<- 0
        isGameOver()

        for(i in 1:3){
          total.1 <- 0 ; total.2 <- 0
          for(j in 1:3){
            total.1 <- total.1 + value[i, j]
            total.2 <- total.2 + value[j, i]
          }
          if(total.1==-1 | total.2==-1){
            break
          }
        }
        total.3 <- value[1, 1] + value[2, 2] + value[3, 3]
        total.4 <- value[1, 3] + value[2, 2] + value[3, 1]

        if(all(is.na(place.na))){
          isGameOver()
        }else if(total.1==-1 | total.2==-1 | total.3==-1 | total.4==-1){
          attack()
        }else{
          defend()
        }
      }
    }
  }
  isGameOver()
}

# Allow users to click on program.

click <- function(tic.tac.toe=ttt){
  name <- tic.tac.toe$name
  mode <- tic.tac.toe$mode
  type <- tic.tac.toe$type

  while(length(place.na)==9){
    mouse.at <- locator(n = 1, type = "n")
    #cat(mouse.at$x,"\t",  mouse.at$y, "\n")
    x.at <- round(mouse.at$x)
    y.at <- round(mouse.at$y)
    #cat(x.at,"\t",  y.at, "\n")
    if(all(is.na(place.na))){
      ttt <<- tic.tac.toe(name, mode, type)
    }else if(x.at > 3.5 | x.at < 0.5 | y.at > 3.5 | y.at < 0.5){
      r <<- r + 1
      title(sub=list("Click outside:Quit / inside:Restart", col="deeppink", font=2, cex=2), line=2)
      if(r==2){
        dev.off()
        break
      }
    }else{
      if(r==1){
        ttt <<- tic.tac.toe(name, mode, type)
      }else{
        aim(x.at, y.at)
      }
    }
  }
}

# Play the game

start <- function(name="Name", mode=0, type=0){
  x11()
  ttt <<- tic.tac.toe(name, mode, type)
  click()
}

#start("name", "mode" = 0 - 2, type = 0,1)


+ minimax.rkt    -- Written in Lazy Racket, implements the general minimax algorythm as
|                   given in Wikipedia.
|                   Knows nothing about games.
V
+ game.rkt       -- Written in Lazy Racket, defines general classes for the game and players.
|                   Knows nothing about tick-tack-toe, only about zero-sum two-player
|                   turn-taking games with perfect information in general.
V
+ tick-tack.rkt  -- Written in Racket, implements the tick-tack-toe game.