Tic Tac Tae Toe

7

(Better known as 3D Tic Tac Toe; I just made up a name that sounded catchy ;-) )

Let two people play this game against each other.

Specifications:

  • Output

    • the easiest way of explaining this will be with an example:

      +-+-+-+-+
      |X| | | |
      +-+-+-+-+
      | |O| | |
      +-+-+-+-+
      | | | | |
      +-+-+-+-+
      |O| | | |
      +-+-+-+-+
      
      +-+-+-+-+
      | | | | |
      +-+-+-+-+
      | |X| | |
      +-+-+-+-+
      | | | |O|
      +-+-+-+-+
      | | | | |
      +-+-+-+-+
      
      +-+-+-+-+
      | | | | |
      +-+-+-+-+
      | |O| | |
      +-+-+-+-+
      |O| |X| |
      +-+-+-+-+
      | | | |X|
      +-+-+-+-+
      
      +-+-+-+-+
      | | | | |
      +-+-+-+-+
      | | | | |
      +-+-+-+-+
      | | | | |
      +-+-+-+-+
      | | | |X|
      +-+-+-+-+
      

      in this case X has won (top left corner of layer #1 to bottom right corner of layer #2)

    • it is also okay to display the four layers horizontally, with one space between them

    • grids may be non-text; in this case you must represent a third dimension in a reasonable way; see also scoring section below
    • after each player's turn, output "{player} wins" if a player wins, "tie" if the board fills up and nobody has won, or the board if the game is not over yet (or notifiy the user of a win/tie in a reasonable way)
  • Input

    • xyz coordinates, 0-based
    • example: input 031 is this location:

      +-+-+-+-+
      | | | | |
      +-+-+-+-+
      | | | | |
      +-+-+-+-+
      | | | | |
      +-+-+-+-+
      | | | | |
      +-+-+-+-+
      
      +-+-+-+-+
      | | | | |
      +-+-+-+-+
      | | | | |
      +-+-+-+-+
      | | | | |
      +-+-+-+-+
      |X| | | |
      +-+-+-+-+
      
      +-+-+-+-+
      | | | | |
      +-+-+-+-+
      | | | | |
      +-+-+-+-+
      | | | | |
      +-+-+-+-+
      | | | | |
      +-+-+-+-+
      
      +-+-+-+-+
      | | | | |
      +-+-+-+-+
      | | | | |
      +-+-+-+-+
      | | | | |
      +-+-+-+-+
      | | | | |
      +-+-+-+-+
      
    • alternate between X and O, starting with X, and prompt the user with the text "{player}'s turn: "

  • Winning condition
    • a player wins if the player gets 4 of their symbol (X or O) in a row, horizontally, vertically, or diagonally. For example, these are all wins (in the input format I have specified) if a player has played any of them:
    • 000, 001, 002, 003
    • 120, 130, 100, 110
    • 000, 110, 220, 330
    • 300, 122, 033, 211
  • Scoring
    • [ws] 10 base points for a working solution
    • [gr] +20 points if you have a graphical (meaning non-text) grid
      • [cc] +30 points if you can click on a cell to go there
    • [cp] +20 points for building a computer player to play against
      • [ai] +10 to 100 points for how good the AI is (judged by me)
      • [ot] +15 points if you can choose between one and two player
    • [ex] +3 points for each interesting or confusing thing in your code that you explain in your answer (interesting, so don't say "n++ adds one to n" and expect to get 3 points. just be reasonable.)
      • for example, you could say "I used [a,b,c].map(max).sum%10 because it takes the foos and converts them into a bar, because {{insert explanation here}}, which I use to frob the baz."
      • if you golf part of or all of your code, you will of course find many more interesting things to explain, but golfing is not required.
    • [wl] +10 points for showing the winning line, for example "000 001 002 003," or highlighting the squares in some way (if you're using a GUI)
    • +5 points for every upvote (this is the part)
    • downvotes don't count
    • include your score in the answer by using the IDs I have placed for the bonuses
      • example: 10 [ws] + 20 [gr] + 20 [cp] = 50
      • don't include upvotes since those change often; I will count those myself
    • I will accept the highest scored answer in one week; if a higher scored answer appears I will accept it
    • if two answers tie, the earlier posted answer will win

Doorknob

Posted 2013-12-01T20:34:48.253

Reputation: 68 138

1

So this is in essential Qubic?

– Johannes Kuhn – 2013-12-01T20:41:51.137

@JohannesKuhn I suppose; that seems to be another version of 3D tic tac toe which I mentioned in the question – Doorknob – 2013-12-01T20:42:52.957

Is the output format strict or would any readable representation of four planes suffice? – Fors – 2013-12-01T21:06:23.007

1Doesn't this make more sense as a code-challenge? In code golf, there will be so many parameters (with 1 letter names) that the code will be unreadable. – DavidC – 2013-12-01T21:10:14.313

@Fors No, the output must be in that format. – Doorknob – 2013-12-01T21:28:17.180

@David Maybe; if this process to be too unreadable or difficult as a code golf, I might make it a coffee challenge (but then I would have to make it a bit harder... ;-) ) – Doorknob – 2013-12-01T21:30:24.670

@DavidCarraher I made it a [tag:code-challenge] – Doorknob – 2013-12-01T23:04:08.763

Good idea. Hopefully you'll have several attempts. – DavidC – 2013-12-01T23:13:51.107

@Doorknob you talk about a graphical grid; if we do that, do we need to allow textual input? – Justin – 2013-12-02T05:59:18.230

@Doorknob So far, the program looks huge (and I'm not near finished...). ~250 lines. I've never seen an answer on this stackexchange site that large. – Justin – 2013-12-02T07:31:34.137

@Quincunx You can allow textual input, or you can allow input by clicking a square to go there (by the [cc] bonus, you would get 30 extra points for that) – Doorknob – 2013-12-02T13:08:27.940

can I not output anything if the game is not finished? Also, can I choose what form to output "wins" or "ties"? – Justin – 2013-12-03T08:17:00.117

Answers

4

HTML/CSS/JS+JQuery

10[ws] + 20[gr] + 30[cc] + 20[cp] + ?[ai] + 15[ot] + 10[wl] + ?[ex] = 105+
Finished the AI, though a really crappy one.

"Is there any way to make it play itself?"
"Yes. Number of players: zero."

For those who don't get the reference: http://www.youtube.com/watch?v=NHWjlCaIrQo
* sign* It's much less cool of a reference now that I have explained it...

JSFiddle


AI

The AI logic goes like this:

  1. If there is a winning move for me, take it.
  2. If there is a winning move for opponent, block it.
  3. Take the "best" unoccupied corner.
  4. Take a random unoccupied space.

HTML

<span>X</span>'s turn
<div>
<table>
    <tr><td></td><td></td><td></td><td></td></tr>
    <tr><td></td><td></td><td></td><td></td></tr>
    <tr><td></td><td></td><td></td><td></td></tr>
    <tr><td></td><td></td><td></td><td></td></tr>
</table>
<table>
    <tr><td></td><td></td><td></td><td></td></tr>
    <tr><td></td><td></td><td></td><td></td></tr>
    <tr><td></td><td></td><td></td><td></td></tr>
    <tr><td></td><td></td><td></td><td></td></tr>
</table>
<table>
    <tr><td></td><td></td><td></td><td></td></tr>
    <tr><td></td><td></td><td></td><td></td></tr>
    <tr><td></td><td></td><td></td><td></td></tr>
    <tr><td></td><td></td><td></td><td></td></tr>
</table>
<table>
    <tr><td></td><td></td><td></td><td></td></tr>
    <tr><td></td><td></td><td></td><td></td></tr>
    <tr><td></td><td></td><td></td><td></td></tr>
    <tr><td></td><td></td><td></td><td></td></tr>
</table>
</div>

JS

var currentPlayer = true, end = false, players = parseInt(prompt('Number of players":'));

function cell(x,y,z){return $('td').eq(x*16+y*4+z);}
function f(i,x){switch(x){case -1: return i; case -2: return 3-i; default: return x;}}
function line(x,y,z){
    var t = $();
    for(var i=0; i<4; i++) t = t.add(cell(f(i,x),f(i,y),f(i,z)));
    return t;
}
function check(x,y,z,t){
    var u = line(x,y,z);
    if(u.map(function(){return $(this).text()==t;}).get().reduce(function(a,b){return a&&b;})) u.css({color: 'red'});
    else return false;
    return end = true;
}
function move(x,y,z){
    if(end) return false;
    var t = currentPlayer?'X':'O';
    cell(x,y,z).text(t);
    if(check(-1,y,z,t)) return true;
    if(check(x,-1,z,t)) return true;
    if(check(x,y,-1,t)) return true;
    if(check(x,-1,-1,t)) return true;
    if(check(x,-1,-2,t)) return true;
    if(check(-1,y,-1,t)) return true;
    if(check(-1,y,-2,t)) return true;
    if(check(-1,-1,z,t)) return true;
    if(check(-1,-2,z,t)) return true;
    if(check(-1,-1,-1,t)) return true;
    if(check(-2,-1,-1,t)) return true;
    if(check(-1,-2,-1,t)) return true;
    if(check(-1,-1,-2,t)) return true;
    currentPlayer = !currentPlayer;
    $('span').text(currentPlayer?'X':'O');
    if(players==2||players==1&&currentPlayer) return false;
    else return ai();
}

function ai_tri(x1,y1,z1,x2,y2,z2,t){
    var u = false;
    for(var i=0;i<4;i++) for(var j=0;j<4;j++)
        if(x1==x2){
            if(cell(x1,i,j).text()==t)
                if(i==y2&&(j==1||j==2) || j==z2&&(i==1||i==2))
                    if(u) return false;
                    else u = true;
                else return false;
        }else if(y1==y2){
            if(cell(i,y2,j).text()==t)
                if(i==x2&&(j==1||j==2) || j==z2&&(i==1||i==2))
                    if(u) return false;
                    else u = true;
                else return false;
        }else{
            if(cell(i,j,z1).text()==t)
                if(i==x2&&(j==1||j==2) || j==y2&&(i==1||i==2))
                    if(u) return false;
                    else u = true;
                else return false;
        }
    return true;
}
function ai_tri2(x1,y1,z1,x2,y2,z2,t){
    var u = false;
    for(var i=0;i<4;i++) for(var j=0;j<4;j++)
        if(x1!=x2){
            if(cell(i,j,j).text()==t)
                if(j==y2&&(i==1||i==2) || i==x2&&(j==1||j==2))
                    if(u) return false;
                    else u = true;
                else return false;
        }else if(y1!=y2){
            if(cell(j,i,j).text()==t)
                if(i==z2&&(j==1||j==2) || j==y2&&(i==1||i==2))
                    if(u) return false;
                    else u = true;
                else return false;
        }else{
            if(cell(j,j,i).text()==t)
                if(i==x2&&(j==1||j==2) || j==z2&&(i==1||i==2))
                    if(u) return false;
                    else u = true;
                else return false;
        }
    return true;
}
function ai_cn(x,y,z,t){
    return ai_tri(x,y,z,x,!y*3,!z*3,t) + ai_tri(x,y,z,!x*3,y,!z*3,t) + ai_tri(x,y,z,!x*3,!y*3,z,t) + ai_tri2(x,y,z,!x*3,y,z,t) + ai_tri2(x,y,z,x,!y*3,z,t) + ai_tri2(x,y,z,x,y,!z*3,t) + ai_tri(!x*3,y,z,x,!y*3,z,t) + ai_tri(!x*3,y,z,x,y,!z*3,t) + ai_tri(x,!y*3,z,!x*3,y,z,t) + ai_tri(x,!y*3,z,x,y,!z*3,t) + ai_tri(x,y,!z*3,!x*3,y,z,t) + ai_tri(x,y,!z*3,x,!y*3,z,t) + ai_tri2(x,!y*3,!z*3,!x*3,!y*3,!z*3,t) + ai_tri2(!x*3,y,!z*3,!x*3,!y*3,!z*3,t) + ai_tri2(!x*3,!y*3,z,!x*3,!y*3,!z*3,t);
}
function ai_check(x,y,z,t){
    var v = [$(),$(),$()], u = line(x,y,z);
    $.each(u.map(function(){return $(this).text();}).get(),function(i,e){if(e=='X') v[0]=v[0].add(u.eq(i)); else if(e=='O') v[1]=v[1].add(u.eq(i)); else v[2]=v[2].add(u.eq(i));});
    if((t==='X'?v[0]:v[1]).length==3 && v[2].length==1) return [true,v[2]];
    else if((t==='X'?v[1]:v[0]).length==3 && v[2].length==1) return [false,v[2]];
    else return null;
}
function ai_check_all(t){
    var u = null, v = null;
    for(var i=0;i<4;i++){
        for(var j=0;j<4;j++){
            if((u=ai_check(i,j,-1,t))!==null) if(u[0] || v!==null) return u[1]; else v = u[1];
            if((u=ai_check(i,-1,j,t))!==null) if(u[0] || v!==null) return u[1]; else v = u[1];
            if((u=ai_check(-1,i,j,t))!==null) if(u[0] || v!==null) return u[1]; else v = u[1];
        }
        if((u=ai_check(i,-1,-1,t))!==null) if(u[0] || v!==null) return u[1]; else v = u[1];
        if((u=ai_check(i,-1,-2,t))!==null) if(u[0] || v!==null) return u[1]; else v = u[1];
        if((u=ai_check(-1,i,-1,t))!==null) if(u[0] || v!==null) return u[1]; else v = u[1];
        if((u=ai_check(-1,i,-2,t))!==null) if(u[0] || v!==null) return u[1]; else v = u[1];
        if((u=ai_check(-1,-1,i,t))!==null) if(u[0] || v!==null) return u[1]; else v = u[1];
        if((u=ai_check(-1,-2,i,t))!==null) if(u[0] || v!==null) return u[1]; else v = u[1];
    }
    if((u=ai_check(-1,-1,-1,t))!==null) if(u[0] || v!==null) return u[1]; else v = u[1];
    if((u=ai_check(-1,-1,-2,t))!==null) if(u[0] || v!==null) return u[1]; else v = u[1];
    if((u=ai_check(-1,-2,-1,t))!==null) if(u[0] || v!==null) return u[1]; else v = u[1];
    if((u=ai_check(-2,-1,-1,t))!==null) if(u[0] || v!==null) return u[1]; else v = u[1];
    return v;
}
function ai(){
    var t1 = currentPlayer?'X':'O', t2 = currentPlayer?'O':'X', u = [0,0,0,0], v = 0, w = ai_check_all();
    if(w!==null) return move(w.closest('table').index(),w.closest('tr').index(),w.index());
    for(var i=0;i<4;i+=3) for(var j=0;j<4;j+=3) for(var k=0;k<4;k+=3)
        if(cell(i,j,k).text()==='' && (v=ai_cn(i,j,k,t2))>u[3]) u = [i,j,k,v];
    if(u[3]>0) return move(u[0],u[1],u[2]);
    u = $('td').filter(function(){return $(this).text()=='';});
    if(u.length===0) return end = true;
    w = u.eq(Math.floor(Math.random()*u.length));
    return move(w.closest('table').index(),w.closest('tr').index(),w.index());
}


$('table').on('click','td',function(){
    if(end || $(this).text() !== '') return true;
    move($(this).closest('table').index(),$(this).closest('tr').index(),$(this).index());
});

if(players == 0) ai();

CSS

body{font-family: Arial, san-serif;}
table{float:left; margin-right: 20px; border-collapse: collapse;}
td{width:20px; height:20px; border:1px solid black; text-align: center;}

TwiNight

Posted 2013-12-01T20:34:48.253

Reputation: 4 187

Wow, very nice! Looking forward to seeing the AI :) +1 – Doorknob – 2013-12-04T23:55:20.783

Why are corners inherently better than other spaces? – Justin – 2013-12-06T17:38:47.813

2

Java

10[ws] + 20[gr] + 30[cc] + 4*3[ex] = 72

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.MouseEvent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.event.MouseInputListener;

public class Game {

    public static void main(String[] args) {
        //Initial threads
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                new Game();
            }
        });
    }
    JFrame frame;
    Grid grid;

    Game() {
        grid = new Grid();
        frame = new JFrame("Tic Tac Tae Toe");

        frame.add(grid);

        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.pack();
        frame.setVisible(true);
    }
}

class Grid extends JPanel implements MouseInputListener {

    static final int BEGIN_X = 50, BEGIN_Y = 50;
    static final int GAP = 50;
    static final int TILE_WIDTH = 40;
    XO[][][] grid;
    int lastX, lastY, lastZ;
    int mouse_x, mouse_y;
    JLabel turn;
    boolean won;

    public Grid() {
        // Just because. I felt like copy-paste.
        grid = new XO[][][]{
            {
                {XO.N, XO.N, XO.N, XO.N},
                {XO.N, XO.N, XO.N, XO.N},
                {XO.N, XO.N, XO.N, XO.N},
                {XO.N, XO.N, XO.N, XO.N}
            },
            {
                {XO.N, XO.N, XO.N, XO.N},
                {XO.N, XO.N, XO.N, XO.N},
                {XO.N, XO.N, XO.N, XO.N},
                {XO.N, XO.N, XO.N, XO.N}
            },
            {
                {XO.N, XO.N, XO.N, XO.N},
                {XO.N, XO.N, XO.N, XO.N},
                {XO.N, XO.N, XO.N, XO.N},
                {XO.N, XO.N, XO.N, XO.N}
            },
            {
                {XO.N, XO.N, XO.N, XO.N},
                {XO.N, XO.N, XO.N, XO.N},
                {XO.N, XO.N, XO.N, XO.N},
                {XO.N, XO.N, XO.N, XO.N}
            }
        };

        lastX = -1;
        lastY = -1;
        lastZ = -1;
        won = false;

        turn = new JLabel("X");

        add(turn);

        this.setPreferredSize(new Dimension(2 * BEGIN_X + 4 * TILE_WIDTH,
                2 * BEGIN_Y + 4 * (4 * TILE_WIDTH + GAP) - GAP));
        this.addMouseListener(this);
        this.addMouseMotionListener(this);
    }

    public XO determineWinner() {
        XO type = grid[lastZ][lastY][lastX];

        //start on same z-plane

        for (int x = 0; x < 4; x++) {
            if (grid[lastZ][lastY][x] != type) {
                break;
            }
            if (x == 3) {
                return type; //type won!
            }
        }
        for (int y = 0; y < 4; y++) {
            if (grid[lastZ][y][lastX] != type) {
                break;
            }
            if (y == 3) {
                return type; //type won!
            }
        }
        //first diagonal
        if (lastX == lastY) {
            for (int x = 0, y = 0; x < 4; x++, y++) {
                if (grid[lastZ][y][x] != type) {
                    break;
                }
                if (x == 3) {
                    return type; //type won!
                }
            }
        } //second diagonal
        else if (lastX == 3 - lastY) {
            for (int x = 0, y = 3; x < 4; x++, y--) {
                if (grid[lastZ][y][x] != type) {
                    break;
                }
                if (x == 3) {
                    return type; //type won!
                }
            }
        }
        //first plane finished
        //now onto changing z's
        //vertical column
        for (int z = 0; z < 4; z++) {
            if (grid[z][lastY][lastX] != type) {
                break;
            }
            if (z == 3) {
                return type; //type won!
            }
        }
        //descending y. note: descending means stair-like (diagonal)
        if (lastZ == lastY) {
            for (int z = 0, y = 0; z < 4; z++, y++) {
                if (grid[z][y][lastX] != type) {
                    break;
                }
                if (z == 3) {
                    return type; //type won!
                }
            }
        } //ascending y
        else if (lastZ == 3 - lastY) {
            for (int z = 0, y = 3; z < 4; z++, y--) {
                if (grid[z][y][lastX] != type) {
                    break;
                }
                if (z == 3) {
                    return type; //type won!
                }
            }
        } //ascending x. note: ascending means stair-like, but other way
        if (lastZ == 3 - lastX) {
            for (int z = 0, x = 3; z < 4; z++, x--) {
                if (grid[z][lastY][x] != type) {
                    break;
                }
                if (z == 3) {
                    return type; //type won!
                }
            }
        } //descending x
        else if (lastZ == lastX) {
            for (int z = 0, x = 0; z < 4; z++, x++) {
                if (grid[z][lastY][x] != type) {
                    break;
                }
                if (z == 3) {
                    return type; //type won!
                }
            }
        }

        //diagonals, changing all three
        if (lastZ == lastX && lastZ == lastY) {
            for (int z = 0, x = 0, y = 0; z < 4; z++, x++, y++) {
                if (grid[z][y][x] != type) {
                    break;
                }
                if (z == 3) {
                    return type; //type won!
                }
            }
        } else if (3 - lastZ == lastX && 3 - lastZ == lastY) {
            for (int z = 3, x = 0, y = 0; z < 4; z--, x++, y++) {
                if (grid[z][y][x] != type) {
                    break;
                }
                if (x == 3) {
                    return type; //type won!
                }
            }
        } else if (lastZ == lastX && lastZ == 3 - lastY) {
            for (int z = 0, x = 0, y = 3; z < 4; z++, x++, y--) {
                if (grid[z][y][x] != type) {
                    break;
                }
                if (z == 3) {
                    return type; //type won!
                }
            }
        } else if (lastZ == 3 - lastX && lastZ == lastY) {
            for (int z = 0, x = 3, y = 0; z < 4; z++, x--, y++) {
                if (grid[z][y][x] != type) {
                    break;
                }
                if (z == 3) {
                    return type; //type won!
                }
            }
        }

        //check for tie
        out: for (int z = 0; z < 4; z++){
            for (int y = 0; y < 4; y++){
                for (int x = 0; x < 4;x++){
                    if (grid[z][y][x] == XO.N){
                        break out;
                    }
                }
            }
            if (z == 3){
                return XO.N;
            }
        }

        return null;
    }

    @Override
    public void paintComponent(Graphics graphics) {
        super.paintComponent(graphics);

        Graphics2D g = (Graphics2D) graphics;
        g.setColor(Color.BLACK);

        //Looks a little nicer, but this could be left out.
        g.setFont(new Font("Courier New", Font.BOLD, 20));

        //paint grid rectangles.
        for (int z = 0; z < 4; z++) {
            int begin_y = BEGIN_Y + z * (4 * TILE_WIDTH + GAP);

            //draw left line
            g.drawLine(BEGIN_X, begin_y,
                    BEGIN_X, begin_y + 4 * TILE_WIDTH);

            //draw bottom line
            g.drawLine(BEGIN_X, begin_y + 4 * TILE_WIDTH,
                    BEGIN_X + 4 * TILE_WIDTH, begin_y + 4 * TILE_WIDTH);

            for (int y = 0; y < 4; y++) {
                for (int x = 0; x < 4; x++) {
                    //draw individual grid lines
                    g.drawLine(BEGIN_X + (x + 1) * TILE_WIDTH, begin_y + y * TILE_WIDTH,
                            BEGIN_X + (x + 1) * TILE_WIDTH, begin_y + (y + 1) * TILE_WIDTH);
                    g.drawLine(BEGIN_X + x * TILE_WIDTH, begin_y + y * TILE_WIDTH,
                            BEGIN_X + (x + 1) * TILE_WIDTH, begin_y + y * TILE_WIDTH);

                    //if this was the last chosen square...
                    if (lastX == x && lastY == y && lastZ == z) {
                        g.setColor(Color.ORANGE);
                        //fill it orange! (not required, but I think it looks nice)
                        g.fillRect(BEGIN_X + x * TILE_WIDTH + 1,
                                begin_y + y * TILE_WIDTH + 1,
                                TILE_WIDTH - 1, TILE_WIDTH - 1);
                        g.setColor(Color.BLACK);
                    }

                    //if mouse is over this square...
                    if ((BEGIN_X + x * TILE_WIDTH) < mouse_x
                            && (BEGIN_X + (x + 1) * TILE_WIDTH) > mouse_x
                            && (begin_y + y * TILE_WIDTH) < mouse_y
                            && (begin_y + (y + 1) * TILE_WIDTH) > mouse_y) {
                        g.setColor(new Color(100, 100, 255));
                        //fill it blue! (not required, but I think it looks nice)
                        g.fillRect(BEGIN_X + x * TILE_WIDTH + 1,
                                begin_y + y * TILE_WIDTH + 1,
                                TILE_WIDTH - 1, TILE_WIDTH - 1);
                        g.setColor(Color.BLACK);
                    }

                    //paint value of tile
                    g.drawString(grid[z][y][x].val,
                            BEGIN_X + x * TILE_WIDTH + TILE_WIDTH / 2,
                            begin_y + y * TILE_WIDTH + TILE_WIDTH / 2);

                }
            }
        }
    }

    @Override
    public void mouseClicked(MouseEvent e) {
        if (!won) {
            int x = e.getX(), y = e.getY();
            x -= BEGIN_X;
            x /= TILE_WIDTH;

            y -= BEGIN_Y;

            int z = 0;
            while (y > 4 * TILE_WIDTH) {
                z++;
                y -= 4 * TILE_WIDTH + GAP;
            }

            y /= TILE_WIDTH;

            //**********************************
            //DOES NOT ALWAYS GET PAST HERE!
            //**********************************
            if (x < 0 || y < 0 || z < 0 || x >= 4 || y >= 4 || z >= 4) {
                return;
            }
            if (grid[z][y][x] == XO.N) {
                grid[z][y][x] = XO.valueOf(turn.getText());
            } else {
                return;
            }

            lastX = x;
            lastY = y;
            lastZ = z;

            XO who_won = determineWinner();
            if (who_won == null){
                if (turn.getText().equals("X")) {
                    turn.setText("O");
                } else {
                    turn.setText("X");
                }
            }
            else if (who_won != XO.N) {
                turn.setText(who_won.toString() + " wins");
            } else{
                turn.setText("tie");
            }
            repaint();
        }
    }

    @Override
    public void mousePressed(MouseEvent e) {
    }

    @Override
    public void mouseReleased(MouseEvent e) {
    }

    @Override
    public void mouseEntered(MouseEvent e) {
    }

    @Override
    public void mouseExited(MouseEvent e) {
    }

    @Override
    public void mouseDragged(MouseEvent e) {
    }

    @Override
    public void mouseMoved(MouseEvent e) {
        mouse_x = e.getX();
        mouse_y = e.getY();
        repaint();
    }
}

enum XO {

    X("X"), O("O"), N(" ");
    String val;

    XO(String c) {
        val = c;
    }
}

This is a working solution, but I will work on it a lot more.

[ex]:

  • runs using Initial Threads
  • informs player's whose turn it is (letter at top of window, will be improved) using a JLabel called turn. This label is also used to say who won.
  • shows the last move by storing the last x, y and z values and painting the corresponding square orange during paintComponent()
  • shows the location of the mouse pointer (blue square) by computing it during paintComponent()

Screenshot:

Screenshot of a finished game

Justin

Posted 2013-12-01T20:34:48.253

Reputation: 19 757

your explanations are not explanations... except for the first one, maybe. all you're doing is saying what your program does. you actually have to explain how it works... +1 anyway for being the first answer :D – Doorknob – 2013-12-03T13:59:22.117

@Doorknob do the comments in code count? For example, I have many comments in my "check if won" algorithm. They don't make sense separate from the code. – Justin – 2013-12-05T21:29:36.690

just put [ex] next to the interesting comments or something and refer to them in your post I guess – Doorknob – 2013-12-05T21:46:02.897

Right now I feel like stealing your answer and redo it in groovy :) Groovy is essentially Java (and uses Java libraries), but with shorter syntax, ie. for (int z = 0; z < 4; z++) can be written as for (z in 0..3) – Fels – 2013-12-06T12:16:43.070

@Fels Why do you feel so? First, doing so is exactly what you said: stealing. Second, it is more fun to code it yourself. If you happen to end up with the same code, the good. Third, my code is not optimal. – Justin – 2013-12-06T17:37:15.163

-1

Ti-Basic 84

10[ws]+20[gr]+20[cp]+10[ai]+15[ot]+3[ex]=78

Programmable and playable from a Ti-84 calculator. +100?

Also, -> represents the STO-> button.

:Disp LOOKS-LIKE-THIS-MIGHT-BE-SOMETHING-INTERESTING-IN-THE-CODE
:ClrHome
:Disp "TIC TAC TAE TOE"
:Disp "HOW MANY PLAYERS"
:Input P
:If P=1 :Disp "YOUR OPPONENTS NAME IS AI BOB"
:If P=2 :Disp "YOUR OPPONENTS NAME IS AI BOB EVEN THOUGH YOU WANT TO PLAY TWO PLAYERS"
:Horizontal -10
:Horizontal -5
:Horizontal 0
:Horizontal 5
:Horizontal 10
:Vertical -10
:Vertical -5
:Vertical 0
:Vertical 5
:Vertical 10
:0->A
:Lbl M
:Disp "YOUR MOVE X-POS"
:Input X
:Disp "YOUR MOVE Y-POS"
:Input Y
:Circle(2,X,Y)
:Circle(2,rand,rand)
:Disp "UGH, HE DIDNT ALIGN IT CORRECTLY"
:Disp "YOU MIGHT WANT TO STOP PLAYING"
:If A=>4 :Goto W
:If A<4 :A+1->A
:Goto M
:Lbl W
:Disp "HAVE YOU WON, 1 IS YES, 0 IS NO"
:Input W
:If W=1 :Goto E
:Goto M
:Lbl E
:Disp "YOU HAVE WON! GOOD JOB!"
:Disp "ENTER TO EXIT"
:Pause

Something interesting: the line of code at the beginning (:Disp LOOKS-LIKE-THIS-MIGHT-BE-SOMETHING-INTERESTING-IN-THE-CODE) prints 0 and then is immediately cleared from the screen.

Feel free to give me more points for my AI's skill, but I'm pretty sure you'll stick with 10.

Timtech

Posted 2013-12-01T20:34:48.253

Reputation: 12 038

...you can't ask the player if he has won. That's obviously cheating. You didn't display the grid correctly either. You can't choose between one and two player. Pretty sure this is a joke, but still... -1 – Doorknob – 2013-12-04T23:56:34.680

@Doorknob The grid actually displays – Timtech – 2013-12-05T00:03:37.600

but you don't even display any of the symbols! this is clearly a wrong answer, even though I don't know any TI-basic at all – Doorknob – 2013-12-05T00:04:25.507

@Doorknob A circle represents O – Timtech – 2013-12-05T00:05:28.467

okay, but you're just displaying the circle in a random position and you're not even displaying any of the symbols correctly at all. commenting further seems pointless, so I'm just going to stop now – Doorknob – 2013-12-05T00:06:37.740

@Doorknob You don't have to reply, but for other people - it actually asks you where you want to place your move, both X and Y, then draws it there. – Timtech – 2013-12-05T00:08:46.387

just for future readers: it doesn't even remember where you placed it... and it never shows the opponent's symbol... and it's just... not even a game (this is my last comment here :P) – Doorknob – 2013-12-05T00:10:35.450

@FutureReaders The calculator knows where you place it (it just has short term memory) – Timtech – 2013-12-05T00:15:23.163