Advertisement
NovaYoshi

Reversi

Mar 26th, 2015
135
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Java 18.26 KB | None | 0 0
  1. package reversi;
  2.  
  3. import java.util.*;
  4. import java.awt.*;
  5. import java.awt.event.*;
  6. import java.awt.geom.*;
  7. import javax.swing.*;
  8.  
  9. /**
  10.  * SquareValue represents one of the three states a square on the board can be
  11.  * in
  12.  */
  13. enum SquareValue {
  14.  
  15.     EMPTY,
  16.     PLAYER,
  17.     CPU;
  18. }
  19.  
  20. /**
  21.  * Move represents a move on the board
  22.  */
  23. class Move implements Comparable<Move> {
  24.  
  25.     private int x;
  26.     private int y;
  27.     private SquareValue player;
  28.     private int total;
  29.     private boolean directionUsed[];
  30.     private int directionRun[];
  31.  
  32.     /**
  33.      * Constructs a new move
  34.      *
  35.      * @param board board to put the move on
  36.      * @param x X position on the board
  37.      * @param y Y position on the board
  38.      * @param player player making the move
  39.      */
  40.     public Move(Board board, int x, int y, SquareValue player) {
  41.         this.x = x;
  42.         this.y = y;
  43.         this.player = player;
  44.         directionUsed = new boolean[8];
  45.         directionRun = new int[8];
  46.         total = 0;
  47.         // check for the opposite of the move's color
  48.         SquareValue checkValue = player == SquareValue.CPU ? SquareValue.PLAYER : SquareValue.CPU;
  49.         for (int i = 0; i < directionUsed.length; i++) {
  50.             int length = board.getRun(x, y, i, checkValue);
  51.             directionRun[i] = length;
  52.             directionUsed[i] = length > 0;
  53.             total += length;
  54.         }
  55.     }
  56.  
  57.     /**
  58.      * Gets the X position of the move
  59.      *
  60.      * @return the X position
  61.      */
  62.     public int getX() {
  63.         return x;
  64.     }
  65.  
  66.     /**
  67.      * Gets the Y position of the move
  68.      *
  69.      * @return the Y position
  70.      */
  71.     public int getY() {
  72.         return y;
  73.     }
  74.  
  75.     /**
  76.      * Gets the player associated with the move
  77.      *
  78.      * @return the player
  79.      */
  80.     public SquareValue getPlayer() {
  81.         return player;
  82.     }
  83.  
  84.     /**
  85.      * Checks if a given direction is used
  86.      *
  87.      * @param direction direction to check
  88.      * @return whether the direction is used
  89.      */
  90.     public boolean isDirectionUsed(int direction) {
  91.         return directionUsed[direction];
  92.     }
  93.  
  94.     /**
  95.      * Gets the amount of possible flips are in a given direction
  96.      *
  97.      * @param direction direction to check
  98.      * @return amount of flips in that direction
  99.      */
  100.     public int getDirectionRun(int direction) {
  101.         return directionRun[direction];
  102.     }
  103.  
  104.     /**
  105.      * Gets the total number of flips in the move
  106.      *
  107.      * @return total number of flips
  108.      */
  109.     public int getTotal() {
  110.         return total;
  111.     }
  112.  
  113.     /**
  114.      * Checks if the move is valid or not
  115.      *
  116.      * @return whether or not the move is valid
  117.      */
  118.     public boolean isValid() {
  119.         return total > 0;
  120.     }
  121.  
  122.     /**
  123.      * Compares two moves
  124.      *
  125.      * @param other other move
  126.      * @return result of the comparison with the other move
  127.      */
  128.     @Override
  129.     public int compareTo(Move other) {
  130.         return this.total - other.total;
  131.     }
  132. }
  133.  
  134. /**
  135.  * Represents a board full of squares
  136.  */
  137. class Board {
  138.  
  139.     public static final int WIDTH = 8;
  140.     public static final int HEIGHT = 8;
  141.  
  142.     // the dir_ tables are offsets for moving in a direction on the board
  143.     public static final int dirX[] = {1, 1, 0, -1, -1, -1, 0, 1};
  144.     public static final int dirY[] = {0, 1, 1, 1, 0, -1, -1, -1};
  145.     private SquareValue squares[][];
  146.  
  147.     /**
  148.      * Applies a given move to the board
  149.      *
  150.      * @param move move to apply
  151.      */
  152.     void applyMove(Move move) {
  153.         SquareValue value = move.getPlayer();
  154.         squares[move.getX()][move.getY()] = value;
  155.         for (int i = 0; i < 8; i++) {
  156.             if (move.isDirectionUsed(i)) {
  157.                 int x = move.getX();
  158.                 int y = move.getY();
  159.                 for (int length = move.getDirectionRun(i); length > 0; length--) {
  160.                     x += dirX[i];
  161.                     y += dirY[i];
  162.                     squares[x][y] = value;
  163.                 }
  164.             }
  165.         }
  166.     }
  167.  
  168.     /**
  169.      * Sets the value of a square on the board
  170.      *
  171.      * @param x X position
  172.      * @param y Y position
  173.      * @param value value to set
  174.      */
  175.     public void setSquare(int x, int y, SquareValue value) {
  176.         squares[x][y] = value;
  177.     }
  178.  
  179.     /**
  180.      * Gets the value of a given square
  181.      *
  182.      * @param x X position
  183.      * @param y Y position
  184.      * @return value of the square
  185.      */
  186.     public SquareValue getSquare(int x, int y) {
  187.         return squares[x][y];
  188.     }
  189.  
  190.     /**
  191.      * Checks if a given X and Y position are actually on the board
  192.      *
  193.      * @param x X position
  194.      * @param y Y position
  195.      * @return whether or not the square is on the board
  196.      */
  197.     public static boolean isSquareOnBoard(int x, int y) {
  198.         return x >= 0 && x < WIDTH && y >= 0 && y < HEIGHT;
  199.     }
  200.  
  201.     /**
  202.      * Gets the total for a specific player
  203.      *
  204.      * @param value value to count towards the total
  205.      * @return total
  206.      */
  207.     public int getPlayerTotal(SquareValue value) {
  208.         int total = 0;
  209.         for (SquareValue col[] : squares) {
  210.             for (SquareValue cell : col) {
  211.                 total += cell == value ? 1 : 0;
  212.             }
  213.         }
  214.         return total;
  215.     }
  216.  
  217.     /**
  218.      * Get the number of flippable circles in a given direction
  219.      *
  220.      * @param x X position
  221.      * @param y Y position
  222.      * @param direction direction to check in
  223.      * @param value color of circles to check for
  224.      * @return number of flippable circles
  225.      */
  226.     public int getRun(int x, int y, int direction, SquareValue value) {
  227.         // starting at a non-empty square is invalid
  228.         if (squares[x][y] != SquareValue.EMPTY) {
  229.             return 0;
  230.         }
  231.  
  232.         // starts at zero because the loop will increase it to 0
  233.         int count = -1;
  234.         do {
  235.             count++;
  236.             x += dirX[direction];
  237.             y += dirY[direction];
  238.             if (!isSquareOnBoard(x, y)) { // not valid if it goes off the board
  239.                 return 0;
  240.             }
  241.         } while (squares[x][y] == value);
  242.  
  243.         // the next square after the run can't be empty
  244.         // because there needs to be another square on the end
  245.         if (squares[x][y] == SquareValue.EMPTY) {
  246.             return 0;
  247.         }
  248.         return count;
  249.     }
  250.  
  251.     /**
  252.      * Constructs a new board
  253.      */
  254.     public Board() {
  255.         squares = new SquareValue[WIDTH][];
  256.         for (int x = 0; x < WIDTH; x++) {
  257.             squares[x] = new SquareValue[HEIGHT];
  258.             for (int y = 0; y < HEIGHT; y++) {
  259.                 squares[x][y] = SquareValue.EMPTY;
  260.             }
  261.         }
  262.     }
  263.  
  264. }
  265.  
  266. /**
  267.  * Main class that simply sets up the frame
  268.  */
  269. public class Main {
  270.  
  271.     public static void main(String[] args) {
  272.         MyFrame frame = new MyFrame();
  273.         ReversiPanel panel = new ReversiPanel(frame);
  274.         frame.add(panel);
  275.         frame.setVisible(true);
  276.     }
  277.  
  278. }
  279.  
  280. /**
  281.  * Holder for MyPanel
  282.  */
  283. class MyFrame extends JFrame {
  284.  
  285.     public static final int WIDTH = 400;
  286.     public static final int HEIGHT = 400;
  287.  
  288.     public MyFrame() {
  289.         super();
  290.         setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
  291.         setSize(WIDTH, HEIGHT);
  292.         setResizable(false);
  293.     }
  294. }
  295.  
  296. /**
  297.  * Contains most of the Reversi implementation
  298.  */
  299. class ReversiPanel extends JPanel {
  300.  
  301.     private Board board;
  302.     private int mouseX, mouseY; // positions in pixels
  303.     private int mouseBoardX = -1, mouseBoardY = -1; // positions on the board
  304.     private boolean mouseEntered = false; // mouse is on the window
  305.     private Move move = null;
  306.     private JFrame parent;
  307.  
  308.     /**
  309.      * Gets a random number from 0 to max
  310.      *
  311.      * @param max limit for the random numbers
  312.      * @return a random number
  313.      */
  314.     private int random(int max) {
  315.         return (int) (Math.random() * max);
  316.     }
  317.  
  318.     /**
  319.      * Try all possible moves
  320.      *
  321.      * @param value player to check for
  322.      * @return all possible moves in an array
  323.      */
  324.     private Move[][] getAllPossibleMoves(SquareValue value) {
  325.         Move[][] out = new Move[Board.WIDTH][];
  326.         for (int x = 0; x < Board.WIDTH; x++) {
  327.             out[x] = new Move[Board.HEIGHT];
  328.             for (int y = 0; y < Board.HEIGHT; y++) {
  329.                 out[x][y] = new Move(board, x, y, value);
  330.             }
  331.         }
  332.         return out;
  333.     }
  334.  
  335.     /**
  336.      * Makes an ArrayList of all the valid moves in a 2D array
  337.      *
  338.      * @param all array of moves
  339.      * @return the list of moves
  340.      */
  341.     private ArrayList<Move> getValidMovesOnly(Move[][] all) {
  342.         ArrayList<Move> valid = new ArrayList<>();
  343.         // add all valid moves to list
  344.         for (int x = 0; x < Board.WIDTH; x++) {
  345.             for (int y = 0; y < Board.HEIGHT; y++) {
  346.                 if (all[x][y].isValid()) {
  347.                     valid.add(all[x][y]);
  348.                 }
  349.             }
  350.         }
  351.         return valid;
  352.     }
  353.  
  354.     /**
  355.      * Do the computer's turn
  356.      */
  357.     private void doComputerMove() {
  358.         Move all[][] = getAllPossibleMoves(SquareValue.CPU);
  359.         // take the corners if possible
  360.         if (all[0][0].isValid()) {
  361.             board.applyMove(all[0][0]);
  362.         } else if (all[Board.WIDTH - 1][0].isValid()) {
  363.             board.applyMove(all[Board.WIDTH - 1][0]);
  364.         } else if (all[0][Board.HEIGHT - 1].isValid()) {
  365.             board.applyMove(all[0][Board.HEIGHT - 1]);
  366.         } else if (all[Board.WIDTH - 1][Board.HEIGHT - 1].isValid()) {
  367.             board.applyMove(all[Board.WIDTH - 1][Board.HEIGHT - 1]);
  368.         } else { // pick the move that removes the fewest player's circles
  369.             ArrayList<Move> valid = getValidMovesOnly(all);
  370.             // skip if no valid moves
  371.             if (valid.size() > 0) {
  372.                 Collections.sort(valid);
  373.                 // find the number of moves with the smallest total
  374.                 int max;
  375.                 for (max = 0; max < valid.size() && valid.get(max).getTotal()
  376.                         == valid.get(0).getTotal(); max++);
  377.                 // apply a random move out of these
  378.                 board.applyMove(valid.get(random(max)));
  379.             }
  380.         }
  381.     }
  382.  
  383.     /**
  384.      * Reacts to mouse clicks, enters and exits
  385.      */
  386.     private class MouseHandler extends MouseAdapter {
  387.  
  388.         /**
  389.          * Reacts to a mouse click
  390.          *
  391.          * @param e mouse event
  392.          */
  393.         public void mousePressed(MouseEvent e) {
  394.             if (move != null && move.isValid()) {
  395.                 applyMove();
  396.                 move = null;
  397.             }
  398.             repaint();
  399.         }
  400.  
  401.         /**
  402.          * Reacts to a mouse enter
  403.          *
  404.          * @param e mouse event
  405.          */
  406.         public void mouseEntered(MouseEvent e) {
  407.             mouseEntered = true;
  408.             repaint();
  409.         }
  410.  
  411.         /**
  412.          * Reacts to a mouse exit
  413.          *
  414.          * @param e mouse event
  415.          */
  416.         public void mouseExited(MouseEvent e) {
  417.             mouseEntered = false;
  418.             repaint();
  419.         }
  420.     }
  421.  
  422.     /**
  423.      * Reacts to mouse moving
  424.      */
  425.     private class MouseMotionHandler extends MouseMotionAdapter {
  426.  
  427.         /**
  428.          * Reacts to a mouse move
  429.          *
  430.          * @param e mouse event
  431.          */
  432.         public void mouseMoved(MouseEvent e) {
  433.             mouseX = e.getX();
  434.             mouseY = e.getY();
  435.             updateMove();
  436.             repaint();
  437.         }
  438.     }
  439.  
  440.     /**
  441.      * Resets the board
  442.      */
  443.     private void resetBoard() {
  444.         board = new Board();
  445.         // create starting layout
  446.         board.setSquare(Board.WIDTH / 2 - 0, Board.HEIGHT / 2 - 0, SquareValue.PLAYER);
  447.         board.setSquare(Board.WIDTH / 2 - 1, Board.HEIGHT / 2 - 0, SquareValue.CPU);
  448.         board.setSquare(Board.WIDTH / 2 - 0, Board.HEIGHT / 2 - 1, SquareValue.CPU);
  449.         board.setSquare(Board.WIDTH / 2 - 1, Board.HEIGHT / 2 - 1, SquareValue.PLAYER);
  450.     }
  451.  
  452.     /**
  453.      * Constructs the panel
  454.      */
  455.     public ReversiPanel(JFrame parent) {
  456.         super();
  457.         resetBoard();
  458.         this.parent = parent;
  459.         // add listeners
  460.         addMouseListener(new MouseHandler());
  461.         addMouseMotionListener(new MouseMotionHandler());
  462.     }
  463.  
  464.     /**
  465.      * Resets the game
  466.      */
  467.     public void resetGame() {
  468.         resetBoard();
  469.         repaint();
  470.     }
  471.  
  472.     /**
  473.      * Apply the player's move, and do the computer's move
  474.      */
  475.     public void applyMove() {
  476.         if (move != null && move.isValid()) {
  477.             board.applyMove(move);
  478.             while (true) {
  479.                 doComputerMove();
  480.                 ArrayList<Move> player = getValidMovesOnly(getAllPossibleMoves(SquareValue.PLAYER));
  481.                 ArrayList<Move> cpu = getValidMovesOnly(getAllPossibleMoves(SquareValue.CPU));
  482.                 if (player.size() == 0 && cpu.size() == 0) { // game over
  483.                     GameResultsDialog test = new GameResultsDialog(board.getPlayerTotal(SquareValue.PLAYER),
  484.                             board.getPlayerTotal(SquareValue.CPU), parent, this);
  485.                     test.setVisible(true);
  486.                     return;
  487.                 }
  488.                 if(player.size() != 0) { // keep doing computer turns if player can't move
  489.                     break;
  490.                 }
  491.             }
  492.         }
  493.     }
  494.  
  495.     /**
  496.      * Update the information for the player's move
  497.      */
  498.     public void updateMove() {
  499.         // get size for individual squares
  500.         int width = this.getWidth() / Board.WIDTH;
  501.         int height = this.getHeight() / Board.HEIGHT;
  502.  
  503.         // get place mouse is hovering over
  504.         if (mouseEntered) {
  505.             int newMouseBoardX = mouseX / width;
  506.             int newMouseBoardY = mouseY / height;
  507.             // don't try to calculate moves for out of bounds cels
  508.             if (newMouseBoardX < 0 || newMouseBoardX >= Board.WIDTH
  509.                     || newMouseBoardY < 0 || newMouseBoardY >= Board.HEIGHT) {
  510.                 move = null;
  511.             } // only calculate moves when necessary
  512.             else if (newMouseBoardX != mouseBoardX || newMouseBoardY != mouseBoardY) {
  513.                 mouseBoardX = newMouseBoardX;
  514.                 mouseBoardY = newMouseBoardY;
  515.                 move = new Move(board, mouseBoardX, mouseBoardY, SquareValue.PLAYER);
  516.             }
  517.         } else {
  518.             move = null;
  519.         }
  520.     }
  521.  
  522.     /**
  523.      * Draw the panel
  524.      */
  525.     @Override
  526.     public void paintComponent(Graphics g) {
  527.         // get size for individual squares
  528.         int width = this.getWidth() / Board.WIDTH;
  529.         int height = this.getHeight() / Board.HEIGHT;
  530.  
  531.         // draw the board
  532.         Graphics2D g2 = (Graphics2D) g;
  533.         for (int x = 0; x < Board.WIDTH; x++) {
  534.             for (int y = 0; y < Board.HEIGHT; y++) {
  535.                 if (mouseEntered && mouseBoardX == x && mouseBoardY == y && move != null && move.isValid()) {
  536.                     g2.setColor(new Color(128, 255, 128));
  537.                 } else if (((x ^ y) & 1) == 0) { // checkerboard
  538.                     g2.setColor(new Color(128, 255, 255));
  539.                 } else {
  540.                     g2.setColor(new Color(255, 255, 128));
  541.                 }
  542.                 Rectangle2D r = new Rectangle2D.Double(x * width,
  543.                         y * height, width - 1, height - 1);
  544.                 g2.draw(r);
  545.                 g2.fill(r);
  546.  
  547.                 // draw circle if present
  548.                 SquareValue value = board.getSquare(x, y);
  549.                 if (value == SquareValue.PLAYER) { // set to the proper color
  550.                     g2.setColor(new Color(255, 0, 0));
  551.                 } else if (value == SquareValue.CPU) {
  552.                     g2.setColor(new Color(0, 0, 255));
  553.                 }
  554.                 // draw if square not empty
  555.                 if (value != SquareValue.EMPTY) {
  556.                     Ellipse2D e = new Ellipse2D.Double(x * width + 1,
  557.                             y * height + 1, width - 2, height - 2);
  558.                     g2.draw(e);
  559.                     g2.fill(e);
  560.                 }
  561.             }
  562.         }
  563.  
  564.         // draw move, if needed
  565.         if (move != null && move.isValid()) {
  566.             int middleX = mouseBoardX * width + width / 2;
  567.             int middleY = mouseBoardY * height + height / 2;
  568.  
  569.             // for all 8 directions draw a line if needed
  570.             g2.setColor(new Color(128, 255, 128));
  571.             for (int i = 0; i < 8; i++) {
  572.                 if (move.isDirectionUsed(i)) {
  573.                     int length = move.getDirectionRun(i) + 1;
  574.                     Line2D line = new Line2D.Double(middleX, middleY,
  575.                             middleX + Board.dirX[i] * length * width, middleY + Board.dirY[i] * length * height);
  576.                     g2.draw(line);
  577.                 }
  578.             }
  579.         }
  580.  
  581.     }
  582. }
  583.  
  584. /**
  585.  * Displays game results
  586.  */
  587. class GameResultsDialog extends JDialog {
  588.  
  589.     /**
  590.      * Creates a game results frame
  591.      *
  592.      * @param player player's total
  593.      * @param cpu CPU's total
  594.      * @param otherPanel reference to the panel that created this frame
  595.      */
  596.     public GameResultsDialog(int player, int cpu, JFrame frame, ReversiPanel otherPanel) {
  597.         super(frame, "Results", false);
  598.         setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
  599.         // display score
  600.         JPanel panel = new JPanel();
  601.         panel.add(new JLabel("Player: " + player));
  602.         panel.add(new JLabel("CPU: " + cpu));
  603.         add(panel, BorderLayout.NORTH);
  604.  
  605.         // display winner
  606.         panel = new JPanel();
  607.         if (player > cpu) {
  608.             panel.add(new JLabel("Player wins"));
  609.         } else if (player < cpu) {
  610.             panel.add(new JLabel("CPU wins"));
  611.         } else {
  612.             panel.add(new JLabel("Draw"));
  613.         }
  614.         add(panel, BorderLayout.CENTER);
  615.  
  616.         // display button
  617.         panel = new JPanel();
  618.         JButton button = new JButton("Play again");
  619.         button.addActionListener(new ActionListener() {
  620.             public void actionPerformed(ActionEvent e) {
  621.                 otherPanel.resetGame();
  622.                 dispose();
  623.             }
  624.         });
  625.         panel.add(button);
  626.         add(panel, BorderLayout.SOUTH);
  627.  
  628.         pack();
  629.     }
  630. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement