diff --git a/.gitignore b/.gitignore index 0be1402..987e05b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,11 @@ *.class +.DS_Store +.idea/ +.metadata/ +cs56-games-flood-it.iml +out/ + # Package Files # *.war *.ear diff --git a/build.xml b/build.xml index ba63387..493ad64 100644 --- a/build.xml +++ b/build.xml @@ -8,7 +8,7 @@ updated by: Chris Luo & Kevin Briggs --> - + diff --git a/src/edu/ucsb/cs56/projects/games/flood_it/view/FloodItController.java b/src/edu/ucsb/cs56/projects/games/flood_it/view/FloodItController.java new file mode 100644 index 0000000..a7ef06a --- /dev/null +++ b/src/edu/ucsb/cs56/projects/games/flood_it/view/FloodItController.java @@ -0,0 +1,265 @@ +package edu.ucsb.cs56.projects.games.flood_it.view; + +import java.util.Collections; +import java.util.Arrays; + +/** + * Class for Flood it game Controller + * + * @author Gustav Schoelin + * @author Karl Wang + */ + +public class FloodItController { + + private int numColors; + private int dimension; + private int difficultyLevel; + private int movesLeft; + private int[][] grid; + + /** + * FloodItController constructor creates an instance of the game + * + * @param dimension the grid will be dimension x dimension + * @param numColors the number of colors available + * @param difficultyLevel how difficult the game is + */ + public FloodItController(int dimension, int numColors, int difficultyLevel) { + this.dimension = dimension; + this.numColors = numColors; + this.difficultyLevel = difficultyLevel; + populateGrid(dimension, numColors, difficultyLevel); + + if (difficultyLevel == 1) + this.movesLeft = (int) (calculateBaselineMovesLeft(dimension * dimension, dimension)); + if (difficultyLevel == 2) + this.movesLeft = (int) (calculateBaselineMovesLeft(dimension * dimension, dimension / 2)); + if (difficultyLevel == 3) + this.movesLeft = (int) (calculateBaselineMovesLeft(dimension * dimension, 1)); + } + + /** + * populateGrid populates the grid of a given size and difficulty level + * + * @param dimension the grid will be dimension x dimension + * @param numColors the number of colors in the grid + * @param difficultyLevel a number 1,2,3 representing easy medium or hard + * @return dimensionxdimension matrix of ints represnting the game board + */ + public void populateGrid(int dimension, int numColors, int difficultyLevel) { + if (difficultyLevel == 1) + grid = populateGridEasy(dimension, numColors); + if (difficultyLevel == 2) + grid = populateGridMedium(dimension, numColors); + if (difficultyLevel == 3) + grid = populateGridHard(dimension, numColors); + } + + /** + * populateGridEasy populates an easy grid of a given size + * + * @param dimension the grid will be dimension x dimension + * @param numColors the number of colors in the grid + * @return dimensionxdimension matrix of ints represnting the game board + */ + public int[][] populateGridEasy(int dimension, int numColors) { + int previousColor = (int) (Math.random() * numColors); + int[][] result = new int[dimension][dimension]; + for (int i = 0; i < dimension; i++) { + for (int j = 0; j < dimension; j++) { + if (Math.random() < .5) { + previousColor = (int) (Math.random() * numColors); + } else if (Math.random() < .5) { + if (i > 1) + previousColor = result[i - 1][j]; + } + result[i][j] = previousColor; + } + } + return result; + } + + /** + * populateGridMedium populates a medium grid of a given size + * + * @param dimension the grid will be dimension x dimension + * @param numColors the number of colors in the grid + * @return dimensionxdimension matrix of ints represnting the game board + */ + public int[][] populateGridMedium(int dimension, int numColors) { + int[][] result = new int[dimension][dimension]; + for (int i = 0; i < dimension; i++) { + for (int j = 0; j < dimension; j++) { + result[i][j] = (int) (Math.random() * numColors); + } + } + return result; + } + + + /** + * populateGridHard populates a hard grid of a given size + * + * @param dimension the grid will be dimension x dimension + * @param numColors the number of colors in the grid + * @return dimensionxdimension matrix of ints represnting the game board + */ + public int[][] populateGridHard(int dimension, int numColors) { + int currentColor = (int) (Math.random() * numColors); + int[][] result = new int[dimension][dimension]; + for (int i = 0; i < dimension; i++) { + for (int j = 0; j < dimension; j++) { + if (i == 0 && j == 0) result[i][j] = (int) (Math.random() * numColors); + if (i != 0 && j != 0) { + while (currentColor == result[i - 1][j] || currentColor == result[i][j - 1]) + currentColor = (int) (Math.random() * numColors); + result[i][j] = currentColor; + } else if (i != 0) { + while (currentColor == result[i - 1][j]) + currentColor = (int) (Math.random() * numColors); + result[i][j] = currentColor; + } else if (j != 0) { + while (currentColor == result[i][j - 1]) + currentColor = (int) (Math.random() * numColors); + result[i][j] = currentColor; + } + } + } + return result; + } + + /** + * calculates the number of moves given to the player + * by running random simulations on the grid + * the average of the top results is provided as the recommended number of moves + * + * @return recommended number of moves, not adjusted for difficulty + */ + private double calculateBaselineMovesLeft(int iterations, int tops){ + if(tops > iterations){ + throw new IllegalArgumentException("requesting the average of more result than that is generated"); + } + int[] testResults = new int[iterations]; //the choice of 30 iterations is completely arbitrary. Feel free to change it. + + for(int i = 0; i < testResults.length; i++){ + int[][] testGrid = gridCopy(this.grid); + int movesUsed = 0; + while(!checkWin(testGrid)){ + int newColor; + do{ + newColor = (int) (Math.random() * numColors); + }while(newColor == testGrid[0][0]); + floodIt(0, 0, newColor, testGrid[0][0], testGrid); + movesUsed++; + } + testResults[i] = movesUsed; + } + + Arrays.sort(testResults); + + int sum = 0; + for (int i = 0; i < tops; i++) { + sum += testResults[i]; + } + return (double)sum / tops; + } + + /** + * floodIt redraws the matrix after the player makes a move + * it is adapted from Wikipedia's algorithm: en.wikipedia.org/wiki/Flood_fill + * + * @param x the x location in the matrix + * @param y the y location in the matrix + * @param newColor the new color being painted + * @param oldColor the color to be repainted + */ + public void floodIt(int x, int y, int newColor, int oldColor) { + floodIt(x, y, newColor, oldColor, this.grid); + } + + /** + * floodIt redraws the matrix after the player makes a move + * it is adapted from Wikipedia's algorithm: en.wikipedia.org/wiki/Flood_fill + * + * @param x the x location in the matrix + * @param y the y location in the matrix + * @param newColor the new color being painted + * @param oldColor the color to be repainted + * @param grid the grid to be worked on + */ + private void floodIt(int x, int y, int newColor, int oldColor, int[][] grid) { + if(newColor == oldColor){ + throw new IllegalArgumentException("newColor and oldColor should not be the same!"); + } + if (x < 0 || y < 0 || x >= grid.length || y >= grid.length) return; + if (grid[x][y] != oldColor) return; + grid[x][y] = newColor; + floodIt(x, y + 1, newColor, oldColor, grid); + floodIt(x, y - 1, newColor, oldColor, grid); + floodIt(x + 1, y, newColor, oldColor, grid); + floodIt(x - 1, y, newColor, oldColor, grid); + return; + } + + /** + * checkWin checks if the player has won the game + * + * @return true if win, false if not. + */ + public boolean checkWin() { + return checkWin(this.grid); + } + + /** + * checkWin checks if the player has won the game + * + * @return true if win, false if not. + * @param grid the grid to be worked on + */ + private boolean checkWin(int[][] grid) { + for (int i = 0; i < grid.length; i++) + for (int j = 0; j < grid.length; j++) + if (grid[i][j] != grid[0][0]) return false; + return true; + } + + public int getDifficultyLevel() { + return difficultyLevel; + } + + public int getDimension() { + return dimension; + } + + public int getNumColors() { + return numColors; + } + + public int[][] getGrid() { + return grid; + } + + public Integer getMovesLeft() { + return movesLeft; + } + + public void setMovesLeft(int movesLeft) { + this.movesLeft = movesLeft; + } + + /** + * utility method for copying a grid + * to be used in calculateMovesLeft() + * + * @return a copy of the provided grid + * @param grid the grid to be worked on + */ + private int[][] gridCopy(int[][] grid){ + int [][] newGrid = new int[grid.length][]; + for(int i = 0; i < grid.length; i++){ + newGrid[i] = grid[i].clone(); + } + return newGrid; + } +} diff --git a/src/edu/ucsb/cs56/projects/games/flood_it/view/FloodItGUI.java b/src/edu/ucsb/cs56/projects/games/flood_it/view/FloodItGUI.java index 0641de8..50949d7 100644 --- a/src/edu/ucsb/cs56/projects/games/flood_it/view/FloodItGUI.java +++ b/src/edu/ucsb/cs56/projects/games/flood_it/view/FloodItGUI.java @@ -4,314 +4,130 @@ import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.swing.*; -import java.util.Scanner; /** * Class for the Flood it game JFrame - * includes the main method + * * @author Daniel Ben-Naim * @author Dylan Hanson * @author Sophia Mao * @author Kai Jann * @author Chris Luo * @author Kevin Briggs + * @author Gustav Schoelin + * @author Karl Wang */ -public class FloodItGUI extends JFrame{ - +public class FloodItGUI extends JFrame { + //private variables for all the GUI components - + + private FloodItController controller; private JFrame frame; private Container textContainer; private FloodItGrid gridBoard; - private FloodItInstructGui instructions; + private FloodItInstructGUI instructions; private JTextArea messageArea; private JButton buttonInstruction; private JPanel buttonPanel; private JTextField countdown; private JLabel movesLeft; - private int [][] grid; - private final Color[] colors = {Color.RED, Color.BLUE, Color.GREEN, Color.YELLOW, - Color.MAGENTA, Color.CYAN,Color.ORANGE,Color.BLACK}; - private String[] colorNames = {"Red","Blue","Green","Yellow","Magenta","Cyan","Orange","Black"}; - private int numColors; - private int dimension; - private int difficultyLevel; - private Integer MOVES_LEFT; - - /** - * FloodItGUI constructor creates an instance of the game - * - * @param dimension the grid will be dimension x dimension - * @param numColors the number of colors available - * @param difficultyLevel how difficult the game is - */ - public FloodItGUI(int dimension, int numColors, int difficultyLevel){ - this.dimension = dimension; - this.numColors = numColors; - this.difficultyLevel = difficultyLevel; - } + private final Color[] colors = {Color.RED, Color.BLUE, Color.GREEN, Color.YELLOW, + Color.MAGENTA, Color.CYAN, Color.ORANGE, Color.BLACK}; + private String[] colorNames = {"Red", "Blue", "Green", "Yellow", "Magenta", "Cyan", "Orange", "Black"}; /** - * init initializes the game and draws the board. - * - */ - public void init(){ - //set MovesLeft (scales number of moves based on number of colors and dimension - //selected using 25 moves for a 6 color, 14x14 grid as a baseline. - MOVES_LEFT = (int)Math.floor(dimension*numColors*25/84);//Thanks, autoboxing! - if(difficultyLevel == 1) MOVES_LEFT = (int)Math.floor(MOVES_LEFT*.8); - if(difficultyLevel == 3) MOVES_LEFT = (int)Math.floor(MOVES_LEFT * 2.33); - //set JFrame properties - frame = new JFrame("Flood It! by SM and KJ and KB and CL and DH and DBN"); - frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - frame.setSize(1000,800); - grid = PopulateGrid(dimension, numColors, difficultyLevel); - gridBoard = new FloodItGrid(grid, colors); - buttonInstruction = new JButton("Instructions"); - //set JTextArea properties for the big message returning box - messageArea = new JTextArea(40,20); - messageArea.setEditable(false); - JScrollPane messageScroller = new JScrollPane(messageArea); - messageScroller.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER); - //set JTextField properties for countdown box - countdown = new JTextField(MOVES_LEFT.toString(),2); - countdown.setEditable(false); - //JLabel for the countdown JTextArea - movesLeft = new JLabel("moves left:"); - //Panel that holds the color buttons and countdown - buttonPanel = new JPanel(); - //add Countdown components to buttonPanel - buttonPanel.add(movesLeft); - buttonPanel.add(countdown); - //ALL button properties - for(int i=0; i1) - previousColor = result[i-1][j]; - } - result[i][j] = previousColor; - } - } - return result; - } - - /** - * PopulateGridEasy populates a medium grid of a given size - * - * @param dimension the grid will be dimension x dimension - * @param numColors the number of colors in the grid - * @return dimensionxdimension matrix of ints represnting the game board - */ - public int[][] PopulateGridMedium(int dimension, int numColors){ - int[][] result = new int[dimension][dimension]; - for(int i=0; i16) - { - System.out.println("Invalid input"); - dimension = S.nextInt(); - } - System.out.println("Enter the number of colors you want: (between 3 and 8)"); - numColors = S.nextInt(); - while(numColors<3||numColors>8) - { - numColors = S.nextInt(); - System.out.println("Invalid input"); - } - System.out.println("Enter 1 for easy, 2 for medium, 3 for hard."); - difficultyLevel = S.nextInt(); - while(difficultyLevel<1||difficultyLevel>3) - { - difficultyLevel = S.nextInt(); - System.out.println("Invalid input"); - } - FloodItGUI game = new FloodItGUI(dimension, numColors, difficultyLevel); - game.init(); - } - - /** - * floodIt redraws the matrix after the player makes a move - * it is adapted from Wikipedia's algorithm: en.wikipedia.org/wiki/Flood_fill - * - * @param x the x location in the matrix - * @param y the y location in the matrix - * @param newColor the new color being painted - * @param oldColor the color to be repainted - */ - public void floodIt(int x, int y, int newColor, int oldColor){ - if(x<0 || y<0 || x>=grid.length || y>= grid.length) return; - if(grid[x][y] != oldColor) return; - grid[x][y] = newColor; - floodIt(x, y+1, newColor, oldColor); - floodIt(x, y-1, newColor, oldColor); - floodIt(x+1, y, newColor, oldColor); - floodIt(x-1, y, newColor, oldColor); - return; - } - - /** - * checkWin checks if the player has won the game - * - * @return true if win, false if not. - */ - public boolean checkWin(){ - for(int i=0; i + * This is the utility function of the class for redrawing the * grid when the user makes a move. - * @param grid A NxN Matrix of integers representing colors in an array + * + * @param grid A NxN Matrix of integers representing colors in an array * @param colors an array of Colors */ - public void redrawLabel(int [][] grid, Color[] colors){ - removeAll(); - - setLayout(new GridLayout(grid.length, grid.length)); - for(int j=0; j dimensionsChooser; + private JComboBox numColorsChooser; + private JComboBox difficultyChooser; + private boolean gameStarted = false; + private int dimensions; + private int numColors; + private int difficulty; + + public FloodItStartMenuGUI() { + frame = new JFrame("Welcome to FloodIt!"); + frame.setSize(600, 200); + Dimension dim = Toolkit.getDefaultToolkit().getScreenSize(); + frame.setLocation(dim.width/2-frame.getSize().width/2, dim.height/3-frame.getSize().height/2); + frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); + + JPanel textPanel = new JPanel(); + textPanel.add(new JLabel("
Welcome to FloodIt!
Choose size, number of colors and difficulty

")); + frame.getContentPane().add(BorderLayout.NORTH, textPanel); + + dimensionsChooser = new JComboBox(); + for (int i = 4; i <= 16; i++) { + dimensionsChooser.addItem(i + "x" + i); + } + + numColorsChooser = new JComboBox(); + for (int i = 3; i <= 8; i++) { + numColorsChooser.addItem(i); + } + + String[] difficulties = {"Easy", "Medium", "Hard"}; + difficultyChooser = new JComboBox(difficulties); + + JPanel chooserPanel = new JPanel(); + chooserPanel.add(new JLabel("Size:")); + chooserPanel.add(dimensionsChooser); + chooserPanel.add(new JLabel("Number of colors:")); + chooserPanel.add(numColorsChooser); + chooserPanel.add(new JLabel("Difficulty:")); + chooserPanel.add(difficultyChooser); + frame.getContentPane().add(BorderLayout.CENTER, chooserPanel); + + JPanel buttonPanel = new JPanel(); + JButton startButton = new JButton("Start Game"); + startButton.addActionListener(new startButtonListener()); + JButton exitButton = new JButton(("Exit")); + exitButton.addActionListener(new exitButtonListener()); + + buttonPanel.add(startButton); + buttonPanel.add(exitButton); + + frame.getContentPane().add(BorderLayout.SOUTH, buttonPanel); + + frame.setVisible(true); + } + + public boolean isGameStarted() { + return gameStarted; + } + + public int getDimensions() { + return dimensions; + } + + public int getNumColors() { + return numColors; + } + + public int getDifficulty() { + return difficulty; + } + + class startButtonListener implements ActionListener { + public void actionPerformed(ActionEvent e) { + dimensions = dimensionsChooser.getSelectedIndex() + 4; + numColors = numColorsChooser.getSelectedIndex() + 3; + difficulty = difficultyChooser.getSelectedIndex() + 1; + + gameStarted = true; + frame.setVisible(false); + frame.dispose(); + } + } + + class exitButtonListener implements ActionListener { + public void actionPerformed(ActionEvent e) { + System.exit(0); + } + } +}