import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
import java.io.File;
import java.net.URL;
import java.util.*;
import javax.imageio.ImageIO;
import javax.swing.*;

/*
 * To change this template, choose Tools | Templates and open the template in
 * the editor.
 */
/**
 *
 * @author lubo
 */
public class TicTacToe extends JApplet {

  private ArrayList<Square> squares = new ArrayList<>();
  private Image xImage, oImage;

  public void initBoard() {
    squares = new ArrayList<>();
    Dimension d = getSize();
    double dx = d.getWidth() / 3.0;
    double dy = d.getHeight() / 3.0;
    for (int x = 0; x < 3; x++) {
      for (double y = 0; y < 3; y++) {
        squares.add(new Square(x * dx, y * dy, dx, dy));
      }
    }
    repaint();
  }

  public void init() {
    URL urlX = null;
    URL urlO = null;
    try {
      urlX = new URL(getCodeBase(), "images/x.jpg");
      urlO = new URL(getCodeBase(), "images/o.jpg");
      xImage = ImageIO.read(urlX);
      oImage = ImageIO.read(urlO);
    } catch (Exception exception) {
    }


    initBoard();
    addMouseListener(new MyMouseListener());
  }

  public void paint(Graphics g) {
    super.paint(g);
    Graphics2D g2 = (Graphics2D) g;
    g2.setColor(Color.RED);
    for (Square r : squares) {
      r.draw(g2, xImage, oImage);
    }
  }

  public void computerMove() {
    if (!squares.get(4).hasValue()) { // if middle is empty
      squares.get(4).placeCharacter('o');
      return;
    }
    Square bestRectangle = squares.get(0);
    int best = computeScore(bestRectangle);
    for (Square r : squares) {
      if (computeScore(r) > best) {
        best = computeScore(r);
        bestRectangle = r;
      }
    }
    bestRectangle.placeCharacter('o');
  }

  public boolean isLine(int i, int j, int k, char c) {
    return (squares.get(i).isCharacter(c)
            && squares.get(j).isCharacter(c) && squares.get(k).isCharacter(c));
  }

  public boolean wins(char c) {
    for (int i = 0; i < 3; i++) {
      if (isLine(3 * i, 3 * i + 1, 3 * i + 2, c) || // horizontal line
              (isLine(i, i + 3, i + 6, c))) { //vertical line 
        return true;
      }
    }
    if (isLine(0, 4, 8, c) || isLine(2, 4, 6, c)) { //diagonal
      return true;
    }
    return false;
  }

  public boolean winsWithNextMove(Square r, char c) {
    r.placeCharacter(c);
    if (wins(c)) {
      r.clear();
      return true;
    }
    r.clear();
    return false;
  }

  public int computeScore(Square r) {
    if (r.hasValue()) {
      return 0;
    }
    if (winsWithNextMove(r, 'o')) {
      return 4;
    }
    if (winsWithNextMove(r, 'x')) {
      return 3;
    }
    return 2;
  }

  public boolean isGameOver() {
    if (wins('o')) {
      JOptionPane.showMessageDialog(this, "I win!");
      initBoard();
      return true;
    }
    if (wins('x')) {
      JOptionPane.showMessageDialog(this, "You win!");
      initBoard();
      return true;
    }
    if (isBoardFull()) {
      JOptionPane.showMessageDialog(this, "It's a tie!");
      initBoard();
      return true;
    }
    return false;
  }

  public boolean isBoardFull() {
    for (Square r : squares) {
      if (!r.hasValue()) {
        return false;
      }
    }
    return true;
  }

  class MyMouseListener extends MouseAdapter {

    public void mousePressed(MouseEvent e) {
      for (Square r : squares) {
        if (r.contains(e.getPoint())) {
          if (!r.hasValue()) {
            r.placeCharacter('x');
            repaint();
            if (isGameOver()) {
              return;
            }
            computerMove();
            repaint();
            if (isGameOver()) {
              return;
            }
          }
        }
      }
    }
  }
}

class Square extends Rectangle2D.Double {

  private boolean isX = false;
  private boolean isO = false;

  public boolean isCharacter(char c) {
    if (c == 'x') {
      return isX;
    }
    if (c == 'o') {
      return isO;
    }
    return false;
  }

  public void placeCharacter(char c) {
    if (c == 'x') {
      isX = true;
    }
    if (c == 'o') {
      isO = true;
    }
  }

  public void clear() {
    isX = false;
    isO = false;
  }

  public boolean hasValue() {
    return (isX || isO);
  }

  public Square(double x, double y, double dx, double dy) {
    super(x, y, dx, dy);
  }

  public void draw(Graphics2D g2, Image xImage, Image oImage) {
    g2.draw(super.getBounds2D());
    if (isX) {
      g2.drawImage(xImage, (int) x + 1, (int) y + 1, (int) getWidth() - 2, (int) getHeight() - 2, null);
    }
    if (isO) {
      g2.drawImage(oImage, (int) getX() + 1, (int) y + 1, (int) getWidth() - 2, (int) getHeight() - 2, null);
    }
  }
}