commit fb03d35727d3a5ff4fd75095a9dba6a6ce6c091d Author: Victor Date: Sat Dec 8 22:34:59 2012 +0200 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1c960ef --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +Desktop.ini \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..2a48b19 --- /dev/null +++ b/README.md @@ -0,0 +1 @@ +Java TicTacToe game. \ No newline at end of file diff --git a/src/jtictactoe/Figure.java b/src/jtictactoe/Figure.java new file mode 100644 index 0000000..087d760 --- /dev/null +++ b/src/jtictactoe/Figure.java @@ -0,0 +1,31 @@ +package jtictactoe; + +/** + * + * @author aNNiMON + */ +public class Figure { + + public static final char + EMPTY = ' ', + X = 'X', + O = 'O'; + + protected char figure; + + public Figure() { + this.figure = EMPTY; + } + + public boolean isEmpty() { + return (figure == EMPTY); + } + + public char getFigure() { + return figure; + } + + public void setFigure(char figure) { + this.figure = figure; + } +} diff --git a/src/jtictactoe/GameBoard.java b/src/jtictactoe/GameBoard.java new file mode 100644 index 0000000..32cbf22 --- /dev/null +++ b/src/jtictactoe/GameBoard.java @@ -0,0 +1,117 @@ +package jtictactoe; + +import java.awt.Dimension; +import java.awt.Graphics; +import java.awt.Point; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.awt.image.BufferedImage; +import javax.swing.JPanel; + +/** + * + * @author aNNiMON + */ +public class GameBoard extends JPanel { + private static final int CELL_SIZE = 150; + private static final int OFFSET = 15; + + private BufferedImage strikedHor, strikedVer, strikedDiag, strikedDiagRev; + private BufferedImage background, figureX, figureO; + private Table table; + private TitlePanel title; + private int imgWidth, imgHeight; + + public GameBoard(TitlePanel title) { + this.title = title; + initImages(); + imgWidth = background.getWidth(); + imgHeight = background.getHeight(); + setPreferredSize( new Dimension(imgWidth, imgHeight) ); + addMouseListener(new MouseAdapter() { + @Override + public void mousePressed(MouseEvent evt) { + gameBoardMousePressed(evt); + } + }); + table = new Table(); + table.resetTable(); + } + + @Override + protected void paintComponent(Graphics g) { + super.paintComponent(g); + g.drawImage(background, 0, 0, null); + drawTable(g); + drawStrike(g); + } + + private void drawTable(Graphics g) { + Figure[][] figures = table.getFiguresArray(); + for (int i = 0; i < figures.length; i++) { + for (int j = 0; j < figures[0].length; j++) { + char figure = figures[i][j].getFigure(); + if (figure != Figure.EMPTY) { + BufferedImage image = ( (figure == Figure.X) ? figureX : figureO ); + int x = j * CELL_SIZE + OFFSET + (CELL_SIZE - image.getWidth()) / 2; + int y = i * CELL_SIZE + OFFSET + (CELL_SIZE - image.getHeight()) / 2; + g.drawImage(image, x, y, null); + } + } + } + } + + private void drawStrike(Graphics g) { + int strikeMode = table.getStrikedMode(); + if (strikeMode != Table.MODE_NOT_STRIKED) { + final int strikedSize = strikedHor.getHeight(); + int coord = OFFSET + (CELL_SIZE - strikedSize) / 2; + int strikePos = strikeMode & 0x03; + + if ( (strikeMode & Table.MODE_STRIKE_DIAGONAL) != 0 ) { + g.drawImage(strikedDiag, coord, coord, null); + } else if ( (strikeMode & Table.MODE_STRIKE_DIAGONAL_REVERSE) != 0 ) { + g.drawImage(strikedDiagRev, coord - OFFSET / 2, coord - OFFSET / 2, null); + } else if ( (strikeMode & Table.MODE_STRIKE_HORIZONTAL) != 0 ) { + g.drawImage(strikedHor, coord, coord + strikePos * CELL_SIZE, null); + } else if ( (strikeMode & Table.MODE_STRIKE_VERTICAL) != 0 ) { + g.drawImage(strikedVer, coord + strikePos * CELL_SIZE, coord, null); + } + } + } + + private void gameBoardMousePressed(MouseEvent evt) { + Point id = convertToID( evt.getPoint() ); + //table.computerMove(); + table.setFigure(id.x, id.y); + + if (table.getStrikedMode() == Table.MODE_NOT_STRIKED) title.setMessage(""); + + if (!table.hasMoreMoves()) { + char winner = table.checkWinner(); + if (winner != Figure.EMPTY) { + int winX = (winner == Figure.X) ? 1 : 0; + title.updateWin(winX, 1 - winX); + title.setMessage(winner + " is win"); + } else title.setMessage("Drow"); + } + repaint(); + } + + private void initImages() { + background = Util.getImage("/res/table_bg.jpg"); + figureX = Util.getImage("/res/x.png"); + figureO = Util.getImage("/res/o.png"); + strikedHor = Util.getImage("/res/striked.png"); + strikedVer = Util.getImage("/res/striked_ver.png"); + strikedDiag = Util.getImage("/res/striked_diagonal.png"); + strikedDiagRev = Util.getImage("/res/striked_diagonal_rev.png"); + } + + private Point convertToID(Point point) { + point.x /= imgWidth / 3; + point.y /= imgHeight / 3; + + return point; + } +} diff --git a/src/jtictactoe/Main.java b/src/jtictactoe/Main.java new file mode 100644 index 0000000..4a3df15 --- /dev/null +++ b/src/jtictactoe/Main.java @@ -0,0 +1,32 @@ +package jtictactoe; + +import java.awt.BorderLayout; +import javax.swing.JFrame; + +/** + * + * @author aNNiMON + */ +public class Main extends JFrame { + + public static void main(String[] args) { + new Main().setVisible(true); + + } + + public Main() { + super("JTicTacToe"); + setResizable(false); + setUndecorated(true); + setAlwaysOnTop(true); + setBounds(300, 120, 0, 0); + setDefaultCloseOperation(EXIT_ON_CLOSE); + setLayout(new BorderLayout(0, 0)); + + TitlePanel title = new TitlePanel(this); + add(title, BorderLayout.NORTH); + add(new GameBoard(title)); + + pack(); + } +} diff --git a/src/jtictactoe/Table.java b/src/jtictactoe/Table.java new file mode 100644 index 0000000..4ebe3df --- /dev/null +++ b/src/jtictactoe/Table.java @@ -0,0 +1,201 @@ +package jtictactoe; + +import java.util.Random; + +/** + * + * @author aNNiMON + */ +public class Table { + + public static final int + MODE_NOT_STRIKED = 0, + MODE_STRIKE_HORIZONTAL = 0x04, + MODE_STRIKE_VERTICAL = 0x08, + MODE_STRIKE_DIAGONAL = 0x10, + MODE_STRIKE_DIAGONAL_REVERSE = 0x20; + + private boolean nextMoveIsX; + private Figure[][] table; + private Random random; + private int strikedMode; + + public Table() { + table = new Figure[3][3]; + random = new Random(); + } + + public void resetTable() { + for (int i = 0; i < table.length; i++) { + for (int j = 0; j < table[0].length; j++) { + table[i][j] = new Figure(); + } + } + strikedMode = MODE_NOT_STRIKED; + nextMoveIsX = true; + } + + public int getStrikedMode() { + return strikedMode; + } + + public Figure[][] getFiguresArray() { + return table; + } + + public void setFigure(int x, int y) { + if( (checkWinner() != Figure.EMPTY) || (!hasMoreMoves()) ) { + resetTable(); + return; + } + + boolean xIsNorm = ( (0 <= x) && (x < 3) ); + boolean yIsNorm = ( (0 <= y) && (y < 3) ); + if (xIsNorm && yIsNorm && table[y][x].isEmpty()) { + table[y][x].setFigure(nextMoveIsX ? Figure.X : Figure.O); + nextMoveIsX = !nextMoveIsX; + computerMove(); + } + } + + public void computerMove() { + if( (checkWinner() != Figure.EMPTY) || (!hasMoreMoves()) ) { + //resetTable(); + return; + } + + computerAI(); + nextMoveIsX = !nextMoveIsX; + } + + private void computerAI() { + final Figure[][] array = new Figure[][] { + // Horizontal check + table[0], + table[1], + table[2], + + // Vertical check + { table[0][0], table[1][0], table[2][0] }, + { table[0][1], table[1][1], table[2][1] }, + { table[0][2], table[1][2], table[2][2] }, + + // Diagonal check + { table[0][0], table[1][1], table[2][2] }, + { table[0][2], table[1][1], table[2][0] } + }; + char[] maybeWinnerArray = new char[array.length]; + // Calculate winning situations for computer (zeroes). + for (int i = 0; i < array.length; i++) { + Figure[] figures = array[i]; + char maybeWinner = maybeWinnerComboIs(figures); + maybeWinnerArray[i] = maybeWinner; + if (maybeWinner == Figure.O) { + if (setCompFigure(figures)) return; + } + } + // Calculate winning situations for the player (crosses). + for (int i = 0; i < array.length; i++) { + Figure[] figures = array[i]; + char maybeWinner = maybeWinnerArray[i]; + if (maybeWinner == Figure.X) { + if (setCompFigure(figures)) return; + } + } + + // Trying to put figure to the center. + int x = 1, y = 1; + if(!table[y][x].isEmpty() || (random.nextInt(5) <= 3) ) { + // If it's busy, put to the corners. + int count = 0; + do { + x = random.nextBoolean() ? 0 : 2; + y = random.nextBoolean() ? 0 : 2; + count++; + } while ( (!table[y][x].isEmpty()) && (count < 10)); + + if (count > 9) { + // Put to the remaining cells. + int[][] pair = {{0, 1}, {1, 0}, {1, 2}, {2, 1}}; + count = 0; + do { + x = pair[random.nextInt(pair.length)][0]; + y = pair[random.nextInt(pair.length)][1]; + count++; + } while ( (!table[y][x].isEmpty()) && (count < 10)); + } + } + table[y][x].setFigure(nextMoveIsX ? Figure.X : Figure.O); + } + + public char checkWinner() { + // Horizontal check + for (int y = 0; y < 3; y++) { + if (isWinnerCombo(table[y][0], table[y][1], table[y][2])) { + strikedMode = MODE_STRIKE_HORIZONTAL | y; + return table[y][0].getFigure(); + } + } + // Vertical check + for (int x = 0; x < 3; x++) { + if (isWinnerCombo(table[0][x], table[1][x], table[2][x])) { + strikedMode = MODE_STRIKE_VERTICAL | x; + return table[0][x].getFigure(); + } + } + // Diagonal check + if (isWinnerCombo(table[0][0], table[1][1], table[2][2])) { + strikedMode = MODE_STRIKE_DIAGONAL; + return table[0][0].getFigure(); + } + if (isWinnerCombo(table[0][2], table[1][1], table[2][0])) { + strikedMode = MODE_STRIKE_DIAGONAL_REVERSE; + return table[0][2].getFigure(); + } + // No winner + return Figure.EMPTY; + } + + public boolean hasMoreMoves() { + if (checkWinner() != Figure.EMPTY) return false; + + int emptyFiguresCount = 0; + for (int i = 0; i < table.length; i++) { + for (int j = 0; j < table[0].length; j++) { + if (table[i][j].isEmpty()) emptyFiguresCount++; + } + } + + return (emptyFiguresCount != 0); + } + + private boolean isWinnerCombo(Figure f1, Figure f2, Figure f3) { + if (f1.isEmpty()) return false; + char figure = f1.getFigure(); + return ( (figure == f2.getFigure()) && + (figure == f3.getFigure()) ); + } + + private char maybeWinnerComboIs(Figure[] array) { + int numX = 0, numO = 0; + for (int i = 0; i < array.length; i++) { + if (!array[i].isEmpty()) { + if (array[i].getFigure() == Figure.X) numX++; + else numO++; + } + } + if (numX == 2) return Figure.X; + if (numO == 2) return Figure.O; + return Figure.EMPTY; + } + + private boolean setCompFigure(Figure[] array) { + for (int i = 0; i < array.length; i++) { + if (array[i].isEmpty()) { + array[i].setFigure(Figure.O); + return true; + } + } + return false; + } +} diff --git a/src/jtictactoe/TitlePanel.form b/src/jtictactoe/TitlePanel.form new file mode 100644 index 0000000..3ebc603 --- /dev/null +++ b/src/jtictactoe/TitlePanel.form @@ -0,0 +1,82 @@ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/src/jtictactoe/TitlePanel.java b/src/jtictactoe/TitlePanel.java new file mode 100644 index 0000000..a6c4370 --- /dev/null +++ b/src/jtictactoe/TitlePanel.java @@ -0,0 +1,195 @@ +package jtictactoe; + +import java.awt.Color; +import java.awt.Font; +import java.awt.Frame; +import java.awt.Graphics; +import java.awt.Point; +import java.awt.event.MouseEvent; +import java.awt.image.BufferedImage; +import javax.swing.ImageIcon; +import javax.swing.JFrame; +import javax.swing.JPanel; + +/** + * + * @author aNNiMON + */ +public class TitlePanel extends JPanel { + + private boolean moveForm; + private BufferedImage exitImage, exitImageBW; + private BufferedImage minimizeImage, minimizeImageBW; + private BufferedImage background; + private Point clickedStart; + private final JFrame mainFrame; + private Font messageFont, infoFont; + + private int winX, winO; + private String message; + + + public TitlePanel(JFrame mainFrame) { + this.mainFrame = mainFrame; + background = Util.getImage("/res/title_bg.jpg"); + minimizeImage = Util.getImage("/res/minimize_button.png"); + minimizeImageBW = Util.getImage("/res/minimize_button_bw.png"); + exitImage = Util.getImage("/res/exit_button.png"); + exitImageBW = Util.getImage("/res/exit_button_bw.png"); + moveForm = false; + messageFont = new Font("Arial", Font.BOLD, 60); + infoFont = new Font("Arial", Font.PLAIN, 30); + winX = winO = 0; + message = ""; + initComponents(); + } + + public void updateWin(int winX, int winO) { + this.winX += winX; + this.winO += winO; + } + + public void setMessage(String message) { + this.message = message; + repaint(); + } + + @Override + public void paintComponent(Graphics g) { + super.paintComponent(g); + final int width = background.getWidth(); + + g.drawImage(background, 0, 0, null); + + g.setColor(Color.DARK_GRAY); + g.setFont(infoFont); + + g.drawString("X:"+winX, 5, 110); + String win = "O:" + winO; + g.drawString(win, width - getFontMetrics(infoFont).stringWidth(win) - 5, 110); + + if (!message.isEmpty()) { + g.setFont(messageFont); + int x = getFontMetrics(messageFont).stringWidth(message) / 2; + x = width / 2 - x; + g.drawString(message, x, 110); + } + } + + @SuppressWarnings("unchecked") + // //GEN-BEGIN:initComponents + private void initComponents() { + + exitButton = new javax.swing.JButton(); + minimizeButton = new javax.swing.JButton(); + + setBackground(new java.awt.Color(51, 51, 51)); + setPreferredSize(new java.awt.Dimension(480, 150)); + addMouseListener(new java.awt.event.MouseAdapter() { + public void mousePressed(java.awt.event.MouseEvent evt) { + formMousePressed(evt); + } + public void mouseReleased(java.awt.event.MouseEvent evt) { + formMouseReleased(evt); + } + }); + addMouseMotionListener(new java.awt.event.MouseMotionAdapter() { + public void mouseDragged(java.awt.event.MouseEvent evt) { + formMouseDragged(evt); + } + }); + setLayout(null); + + exitButton.setIcon(new javax.swing.ImageIcon(exitImageBW)); + exitButton.setBorder(null); + exitButton.setBorderPainted(false); + exitButton.setContentAreaFilled(false); + exitButton.addMouseListener(new java.awt.event.MouseAdapter() { + public void mouseEntered(java.awt.event.MouseEvent evt) { + exitButtonMouseEntered(evt); + } + public void mouseExited(java.awt.event.MouseEvent evt) { + exitButtonMouseExited(evt); + } + }); + exitButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + exitButtonActionPerformed(evt); + } + }); + add(exitButton); + exitButton.setBounds(378, 2, 100, 50); + + minimizeButton.setIcon(new javax.swing.ImageIcon(minimizeImageBW)); + minimizeButton.setBorder(null); + minimizeButton.setBorderPainted(false); + minimizeButton.setContentAreaFilled(false); + minimizeButton.addMouseListener(new java.awt.event.MouseAdapter() { + public void mouseEntered(java.awt.event.MouseEvent evt) { + minimizeButtonMouseEntered(evt); + } + public void mouseExited(java.awt.event.MouseEvent evt) { + minimizeButtonMouseExited(evt); + } + }); + minimizeButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + minimizeButtonActionPerformed(evt); + } + }); + add(minimizeButton); + minimizeButton.setBounds(2, 2, 100, 50); + }// //GEN-END:initComponents + + private void exitButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_exitButtonActionPerformed + mainFrame.setVisible(false); + mainFrame.dispose(); + System.exit(0); + }//GEN-LAST:event_exitButtonActionPerformed + + private void minimizeButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_minimizeButtonActionPerformed + mainFrame.setState(Frame.ICONIFIED); + }//GEN-LAST:event_minimizeButtonActionPerformed + + private void exitButtonMouseEntered(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_exitButtonMouseEntered + exitButton.setIcon(new ImageIcon(exitImage)); + }//GEN-LAST:event_exitButtonMouseEntered + + private void exitButtonMouseExited(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_exitButtonMouseExited + exitButton.setIcon(new ImageIcon(exitImageBW)); + }//GEN-LAST:event_exitButtonMouseExited + + private void minimizeButtonMouseEntered(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_minimizeButtonMouseEntered + minimizeButton.setIcon(new ImageIcon(minimizeImage)); + }//GEN-LAST:event_minimizeButtonMouseEntered + + private void minimizeButtonMouseExited(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_minimizeButtonMouseExited + minimizeButton.setIcon(new ImageIcon(minimizeImageBW)); + }//GEN-LAST:event_minimizeButtonMouseExited + + private void formMousePressed(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_formMousePressed + if (evt.getButton() == MouseEvent.BUTTON1) { + moveForm = true; + clickedStart = evt.getPoint(); + } + }//GEN-LAST:event_formMousePressed + + private void formMouseReleased(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_formMouseReleased + moveForm = false; + }//GEN-LAST:event_formMouseReleased + + private void formMouseDragged(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_formMouseDragged + if (moveForm) { + Point moved = evt.getLocationOnScreen(); + moved.translate(-clickedStart.x, -clickedStart.y); + mainFrame.setLocation(moved); + } + }//GEN-LAST:event_formMouseDragged + + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JButton exitButton; + private javax.swing.JButton minimizeButton; + // End of variables declaration//GEN-END:variables + + +} diff --git a/src/jtictactoe/Util.java b/src/jtictactoe/Util.java new file mode 100644 index 0000000..a68e6ba --- /dev/null +++ b/src/jtictactoe/Util.java @@ -0,0 +1,25 @@ +package jtictactoe; + +import java.awt.image.BufferedImage; +import java.io.IOException; +import java.io.InputStream; +import javax.imageio.ImageIO; + +/** + * + * @author aNNiMON + */ +public class Util { + + public static BufferedImage getImage(String path) { + BufferedImage image = null; + try { + InputStream is = Runtime.getRuntime().getClass().getResourceAsStream(path); + image = ImageIO.read(is); + is.close(); + } catch (IOException ex) { + } + return image; + } + +} diff --git a/src/res/exit_button.png b/src/res/exit_button.png new file mode 100644 index 0000000..53ac5d3 Binary files /dev/null and b/src/res/exit_button.png differ diff --git a/src/res/exit_button_bw.png b/src/res/exit_button_bw.png new file mode 100644 index 0000000..4f1dfbf Binary files /dev/null and b/src/res/exit_button_bw.png differ diff --git a/src/res/minimize_button.png b/src/res/minimize_button.png new file mode 100644 index 0000000..8bdaca7 Binary files /dev/null and b/src/res/minimize_button.png differ diff --git a/src/res/minimize_button_bw.png b/src/res/minimize_button_bw.png new file mode 100644 index 0000000..74209c8 Binary files /dev/null and b/src/res/minimize_button_bw.png differ diff --git a/src/res/o.png b/src/res/o.png new file mode 100644 index 0000000..8b8ccd7 Binary files /dev/null and b/src/res/o.png differ diff --git a/src/res/striked.png b/src/res/striked.png new file mode 100644 index 0000000..de85f7d Binary files /dev/null and b/src/res/striked.png differ diff --git a/src/res/striked_diagonal.png b/src/res/striked_diagonal.png new file mode 100644 index 0000000..fd85c26 Binary files /dev/null and b/src/res/striked_diagonal.png differ diff --git a/src/res/striked_diagonal_rev.png b/src/res/striked_diagonal_rev.png new file mode 100644 index 0000000..1eb7dd7 Binary files /dev/null and b/src/res/striked_diagonal_rev.png differ diff --git a/src/res/striked_ver.png b/src/res/striked_ver.png new file mode 100644 index 0000000..97ae2fa Binary files /dev/null and b/src/res/striked_ver.png differ diff --git a/src/res/table_bg.jpg b/src/res/table_bg.jpg new file mode 100644 index 0000000..ab9125a Binary files /dev/null and b/src/res/table_bg.jpg differ diff --git a/src/res/title_bg.jpg b/src/res/title_bg.jpg new file mode 100644 index 0000000..7e76f4b Binary files /dev/null and b/src/res/title_bg.jpg differ diff --git a/src/res/x.png b/src/res/x.png new file mode 100644 index 0000000..ea36c7e Binary files /dev/null and b/src/res/x.png differ