I am working on an assignment called "Graphical Sort", which is basically animating sort algorithm graphically.
I just need help on animating the sorting process.
I tried using Thread, but the program hangs till the threading process is completed, then it shows the final result.
Below are the picture of how my program looks like:
Below is the class of the panel I use to paint on
import javax.swing.*;
import java.awt.*;
import java.util.*;
public class PaintPanel extends JPanel
{
// Create an array of 34 element size
int[] Arr = new int [34];
// Set default X pointer to 20
int x = 50;
// Declare Y pointer to 660
int y = 660;
// Set the length of array to n variable
int n = Arr.length;
/*
* main method
* #param none
* #return none
*/
public PaintPanel ()
{
randomNums ();
}
/*
* Generates random numbers between 50 and 750 and stores it into the Arr variable
* #param none
* #return none
*/
public void randomNums ()
{
// call randomGenerator object
Random randomGenerator = new Random ();
// Loop 33 times = Generates 33 random integers
for (int i = 0 ; i <= 33 ; ++i)
{
// Generate random Number
int randomInt = randomGenerator.nextInt (700);
// Conditional statement, if any number is less than 50, then discard it and generate new number
if (randomInt > 50)
// Assign each random number into Arr Element
Arr [i] = randomInt;
else
{
// Regenerate Random Number
randomInt = randomGenerator.nextInt (700);
// Assign it again
Arr [i] = randomInt;
}
}
}
/*
* Bubble Sort Algorithm
* #param none
* #return none
*/
public void bubble ()
{ //Pre: a is an array with values. It is of size n
//Post: the values in a are put in ascending order
int temp;
int a[] = Arr;
for (int i = 0 ; i < n - 1 ; i++)
{
for (int j = 0 ; j < n - 1 - i ; j++)
{ // compare the two neighbours
if (a [j + 1] < a [j])
{ //swap the neighbours if necessary
temp = a [j];
a [j] = a [j + 1];
a [j + 1] = temp;
}
}
}
}
/*
* Paints 33 rectangle Strips to the screen
* #param Graphics g
* #return none
*/
public void paintComponent (Graphics g)
{
super.paintComponent (g);
// Call Graphics2D Object
Graphics2D g2 = (Graphics2D) g.create ();
// Create Paint Object with gradient Fill
Paint p = new GradientPaint (
0, 0, new Color (0x44A2FF),
getWidth (), 0, new Color (0x0CBEAE),
true
);
// Set the gradient fill to the Graphics2D Object
g2.setPaint (p);
// Loop through the Array and display series of Rectangular Strips
for (int i = 0 ; i < Arr.length ; ++i)
{
// Fill out the Rectangle
g2.fillRect (x, y, Arr [i], 8);
y = y - 15;
}
g2.dispose ();
}
}
What should I use to animate the process. I also want to show which rectangular strips are being compared during the process of sorting.
Thank You
When I wrote a similar program, I created a SortListener interface with two methods: onCompare() and onSwap(). I used the onCompare() to highlite the two elements being compared with a different color and the onSwap() to notify the GUI to repaint istelf.
I also created a 'SortingAlgorithm' abstract class and several subclasses for specific sorting algorithms. The super class defined addSortListener() to allow other classes to register listeners. Then during the sorting algorithm, I called onCompare() immediately after a comparison and onSwap() imediately after two elements were swapped.
Finally, the JPanel where I did my painting implemented the SortListener interface and responded to onCompare() and onSwap() by repainting the animation.
Related
I am about to learn java right now (I came from C++), and I am trying myself in GUI-Programming.
My goal is to create a playable version of "conway's game of life" (which is a good beginner project i think).
What i have managed to do is, that i stored all of my cells in a 2d array (Here is my gode for that)
public class Panel extends JPanel
{
private int PanelX = 1777, PanelY = 1000;
public boolean[][] grid = new boolean[192][108];
public Panel()
{
this.setBackground(Color.black);
this.setSize(PanelX, PanelY);
this.setVisible(true);
}
#Override
protected void paintComponent(Graphics g)
{
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.setColor(Color.green);
prepareGrid(); // sets the entire index of grid[][] to zero for start and reset
drawGrid(g2d); // draws the grid
}
private void prepareGrid()
{
for (int i = 0; i<191; i++)
{
for (int t = 0; t<107; t++)
{
grid[i][t] = false;
}
}
}
private void drawGrid(Graphics g)
{
for (int i=0; i<=191; i++)
{
for (int t=0; t<=107; t++)
{
if (grid[i][t] == false)
{
g.drawRect(i*10, t*10, 10, 10);
}
if(grid[i][t] == true)
{
g.fillRect(i*10, t*10, 10, 10);
}
}
}
}
}
So what it does it creates an 2 dimensional array which stores all cells as either false = no cell, or true = cell. When an array index (for example grid[100][100] is true, it draws a filled rectangle on that position as a "living cell".
To implement the game and the rules of the game now, I need a way to access all the neighbor positions of an index of that 2d array, but I do not know how to to that. Could anyone help me with that ? :)
-> And if you have major optimizations for my code, feel free to write them as well.
Adding to markspace's answer, it is important to ensure that the counting of alive cells does not interfere with their updating (letting cells be born or die) in the same generation. In other words: If you count 3 neighbors of a dead cell, you must not immediately set it to alive, because it must still be counted as dead for its other neighbors.
You could, for example, first count the neighbors for every cell and then, in a second nested loop, update every cell according to its previously counted number of neighbors. But you can do better with a more elaborate algorithm: you do not absolutely need an array of counters of the same size as your grid.
It's just counting, it's not really hard. Use two loops to count all eight adjacent cells, skip the middle one when you find that cell.
/**
* Returns the number of "cells" that are alive and adjacent to
* the cell at the given X,Y coordinates.
*
*/
public int aliveAdjacent( int x, int y ) {
int count = 0;
for( int xd = -1; xd <= 1; xd++ )
for( int yd = -1; yd <= 1; yd++ ) {
if( xd == 0 && yd == 0 ) continue;
if( isAlive( wrap(x+xd, width), wrap(y+yd,heigth) ) ) count++;
}
return count;
}
I implemented different type of sorting (bubble, insertion, selection). Know I want to compare their implementations like the following for each sort (here's an example with the bubble sort) :
For example, here's my bubble sort :
private static int[] bubbleSort(int[] tabToSort) {
int [] tab = tabToSort.clone();
boolean tabSort = false;
while(!tabSort){
tabSort = true;
for(int i = 0; i < tab.length -1; i++){
if(tab[i]> tab[i+1]){
int temp = tab[i+1];
tab[i+1] = tab[i];
tab[i] = temp;
tabSort = false;
}
}
}
return tab;
}
I started the GUI and I placed 1000 random points on it and the line y=x :
#Override
public void paintComponent (Graphics g){
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setColor(Color.BLACK);
Dimension size = getSize();
Insets insets= getInsets();
int w = size.width - insets.left - insets.right;
int h = size.height - insets.top - insets.bottom;
g2d.drawLine(size.width ,0, 0, size.height);
Random r = new Random();
for (int i =0; i < 1000; i++) {
int x = Math.abs(r.nextInt()) % w;
int y = Math.abs(r.nextInt()) % h;
Point p = new Point(x, y);
g2d.drawLine(p.x, p.y, p.x, p.y);
}
}
Here's what I've done :
Now I'm stuck, I have no idea about how to start. Could anyone indicate me the steps/ hints to follow to implement that ?
Thanks :)
You must define what the points mean. Looking at the animation, it looks like the y axis represents a value, whilst the x axis represents the position in the array of that value.
In your paint method, you would then go through the list of items and paint a dot, with the x-point being the position in the array and the y-point being a position on the y-axis. Assuming the values are within a known range.
Also, remember that the y-axis in graphics starts with 0 at the top, so you may have to do some translation of values to coordinates (depending on how you want it to look).
The easiest way would be to convert your paint method into one that uses a predefined List of points as a parameter instead of random points. On each iteration of your sort method pass the sorted array into the paint method and repaint the dots.
You'll need to
Create an int[] array with random values as a member variable. Let's call the array data. You probably want to start with a fixed array size and range of 100 each. You can adjust the values to the window size later, when a simple version is working. It may be even better to stick to a fixed size and range and just scale to the space available in paintComponent, making the behavior independent of the window size.
Change paintComponent to loop over data. The loop index is your x value and data[x] determines the y value.
Test that the code still draws the initial random array. Don't care if it is in the uppler left corner only now, you can fix that when the animation is working.
You'll need to add some kind of sleep() call to the innermost loop of your sort method, so you get a chance to observe the steps. Otherwise, even bubblesort will be too fast to observe. I'd recommend to start with one second (parameter value 1000). Make it faster later when everything works.
Start the bubbleSort method in a new thread and make sure your component gets repainted with each step. This may be the most tricky part. Perhaps hand in the component to the bublleSort method (or make bubbleSort a non-static method of the component) and let it request a repaint() at each step (fortunately, this is one of the few thread safe methods in Swing).
Fine-tune your code: Scale the x and y coordinates by multiplying with the space available and then dividing by the array size or value range. Adjust the sleep time as needed. Add support for different sorting algorithms....
If any of the steps is unclear, add a comment.
I've done this for my bachelorthesis, I did it like this (it's not perfect, but it might help you):
(I removed some unimportant methods/functions from the code below. It's mainly to illustrate how I visualized it. You can replace the GRectangle class by a simple java.awt.Point for example.)
The initialization method gives you an example of how you can find the maximum and minimum value of the data so you know how to transform your datavalues => coordinates.
public class DotVisualisation extends Visualisation {
private ArrayList<GRectangle> m_points;
private Comparable[] m_data;
private Comparable m_maxValue;
private Comparable m_minValue;
private int MAX_HEIGHT; // max height in pixels of visualization
/**
* Creates a new DotVisualisation.<br>
* <br>
* This class is a runnable JComponent that will visualize data as a function.
* The visualisation will plot the data with X and Y coordinates on the window.
* The X coordinate of the point is index of the dataelement.
* The Y coordinate of the point is relative to the value of the dataelement.<br>
* <br>
* This visualisation should be used for medium and large arrays.
*
* #author David Nysten
*/
public DotVisualisation()
{
m_points = new ArrayList<GRectangle>();
MAX_HEIGHT = 150;
}
/**
* Returns the maximum supported dimension by this visualisation.
*
* #return The supported dimension.
*/
public static int getSupportedDimension()
{
return 1;
}
#Override
public Dimension getMaximumSize()
{
return getPreferredSize();
}
#Override
public Dimension getPreferredSize()
{
return new Dimension(m_points.size() + 2, MAX_HEIGHT + 6);
}
#Override
public Dimension getMinimumSize()
{
return getPreferredSize();
}
#Override
public void paintComponent(Graphics g)
{
for(int i = 0; i < m_points.size(); ++i)
m_points.get(i).paintComponent(g);
}
private void swap(int index, int index2) { // See below }
private void initialise()
{
findMinimum();
findMaximum();
m_points.clear();
double multiplier;
int x = 0, y = 0, h;
for(int i = 0; i < m_data.length; ++i)
{
if(m_data[i].compareTo(-1) <= 0)
h = 0;
else
{
Integer value = (Integer) m_data[i];
Integer min = (Integer) m_minValue;
Integer diff = (Integer) m_maxValue - min;
multiplier = MAX_HEIGHT / diff.doubleValue();
h = (int) ((value - min) * multiplier);
}
y = (int) (MAX_HEIGHT - h);
GRectangle r = new GRectangle(x, y, 1, 1); // 1, 1 = width and height
r.setColor(Color.BLACK);
m_points.add(r);
++x;
}
}
private void findMaximum()
{
Comparable max = null;
if(m_data.length > 0)
{
max = m_data[0];
for(int i = 1; i < m_data.length; ++i)
if(m_data[i].compareTo(max) > 0)
max = m_data[i];
}
m_maxValue = max;
}
private void findMinimum()
{
Comparable min = null;
if(m_data.length > 0)
{
min = m_data[0];
for(int i = 1; i < m_data.length; ++i)
if(m_data[i].compareTo(min) < 0)
min = m_data[i];
}
m_minValue = min;
}
}
Take this into account:
Visualizing integers between 0 and 150 on a height of 150 pixels is straightforward. Visualizing a set of integers between the values 565 and 3544545 on a height of 150 is a bit less so.
PS: The code uses the index of the element in the inputarray as the X-coordinate.
PS: The class keeps a reference to the inputarray (m_data variable) but that's ofcourse not needed, you only need it to initialize your points.
PS: My "Visualization" class which is extended by all visualizations, is basicly a JPanel.
PS: The code above is written for positive integers, so will probably need some extra coding to handle negative integers aswell ;).
Then to visualize the actions of the algorithm, I used the observer pattern. The algorithm, for example bubblesort, looked like this:
for(int i = 0; i < size(); ++i)
for(int j = 1; j < size(); ++j)
if(greaterThan(j - 1, j))
swap(j - 1, j);
Where the function swap was defined as follows (simplified version again):
protected void swap(int index1, int index2)
{
if(index1 != index2)
{
incrementSwap(); // counting swaps and visualizing counter
m_command.clear();
m_command.setAction(Action.SWAP);
m_command.addParameter(index1);
m_command.addParameter(index2);
setChanged();
notifyObservers(m_command);
E temp = m_data[index1];
m_data[index1] = m_data[index2];
m_data[index2] = temp;
}
}
Where I notified my observers (visualizations) that a swap occured on index1 and index2. The m_command variable is an instance of the Command-class (wrote it myself) which is just a wrapper for the information needed by the visualization. Which is: the action that occured and the relevant information (indices for a swap-action for example).
So in the visualization i swapped the GRectangles on those indices aswell as their X-coordinates;
private void swap(int index, int index2)
{
if(index == index2)
return;
GRectangle r1 = m_points.get(index);
GRectangle r2 = m_points.get(index2);
int tempX = r1.getX();
r1.setLocation(r2.getX(), r1.getY());
r2.setLocation(tempX, r2.getY());
m_points.set(index, r2);
m_points.set(index2, r1);
}
You can add lines like this:
try {
Thread.sleep(100);
} catch(InterruptedException ignore) {}
to let a thread sleep 100ms before continueing. This might come in handy if it's getting visualized too fast.
So for an array with random integers it might look like this:
And after sorting:
(Ofcourse it's not a straight line because the values in the inputarray were generated at random in this case)
So if you have to - like I had to - allow multiple algorithms to work with the same visualization, I can recommend you to separate the visualization class and the algorithm class and work with an observer pattern to let the visualization update whenever an action occurs (set, swap, ...).
And then you can create something like this for comparisons;
http://i445.photobucket.com/albums/qq179/ultddave/DotVisualizationMany_zps63269d2a.png
http://i445.photobucket.com/albums/qq179/ultddave/DotVisualizationMany2_zps65e96fa9.png
Good luck!
I've been working on this program for a couple weeks now, adding components to an applet in order to output a simple tic tac toe game. I've gotten some of it to work, but as of now, when you run, all it does is that you click it once, the CPU projects its mark in the upper left corner, and then you can click anywhere else and it automatically says you win. I can't get the CPU to keep playing. I don't expect anyone to tell me what to do, but I'm just confused as to which method I need to work on in order to get the CPU to respond. My professor has left some very helpful pseudocode, but I still don't quite understand. I've been working with the method "gameEnd," checking for winners horizontally, vertically and diagonally to see if that's the source to getting the game to continue beyond just two marks, but it's not working. Anyone got any suggestions? Thanks.
import java.awt.Color;
import java.awt.Event;
import java.awt.Font;
import java.awt.Graphics;
import java.util.Random;
public class TicTacToe extends java.applet.Applet {
// Ignore this constant
private static final long serialVersionUID = 1942709821640345256L;
// You can change this boolean constant to control debugging log output
private static final boolean DEBUGGING = false;
// Constants
// Size of one side of the board in pixels
private static final int BOARD_SIZE_PIXELS = 600;
// Number of squares on one side of the board
private static final int SQUARES = 3;
// Diameter of the circle drawn in each square
private static final int CIRCLE_WIDTH = 90;
// Colors to be used in the game
private static final Color BACKGROUND_COLOR = Color.WHITE;
private static final Color SQUARE_BORDER_COLOR = Color.BLACK;
private static final Color GAME_OVER_MESSAGE_COLOR = Color.BLACK;
private static final Color HUMAN_COLOR = Color.RED;
private static final Color HUMAN_WINNING_COLOR = Color.MAGENTA;
private static final Color CPU_COLOR = Color.BLUE;
private static final Color CPU_WINNING_COLOR = Color.CYAN;
// Status constant values for the game board
private static final int EMPTY = 0;
private static final int HUMAN = 1;
private static final int HUMAN_WINNING = 2;
private static final int CPU = -1;
private static final int CPU_WINNING = -2;
// String displayed when the game ends
private static final String GAME_WIN_MESSAGE = "You win! Click to play again.";
private static final String GAME_LOSE_MESSAGE = "You lose......Click to play again.";
private static final String GAME_DRAW_MESSAGE = "No one wins? Click to play again...";
// Instance variables that control the game
// Whether or not the user just clicked the mouse
private boolean mouseClicked = false;
// Whether or not to start the game again
private boolean restart = false;
// Whether or not the CPU should start playing a move.
// USED ONLY WHEN THE CPU PLAYS FIRST!
private boolean onFirstMove = false;
// The column and row of the SQUARE the user clicked on
private int xMouseSquare; // column
private int yMouseSquare; // row
// The width (and height) of a single game square
private int squareWidth = BOARD_SIZE_PIXELS / SQUARES;
// An array to hold square status values on the board.
// The status values can be EMPTY, HUMAN, CPU or other values
private int[][] gameBoard;
// The column and row of the SQUARE the CPU player will move on
private int xCPUMove;
private int yCPUMove;
// Add the rest of your instance variables here, if you need any. (You won't
// need to, but you may if you want to.)
// Ignore these instance variables
// CPUinMove represents if the CPU is thinking (generating the CPU move).
// If it is true, it means the CPUMove() method is running and no new move
// should be added
private boolean CPUinMove;
// Methods that you need to write:
/*
* Pre: x and y are x-coordinate and y-coordinate where the user just
* clicks. squareWidth is the width (and height) of a single game square.
*
* Post: xMouseSquare and yMouseSquare are set to be the column and row
* where the user just clicked on (depending on x and y).
*
* Hint: You need only two statements in this method.
*/
// Setting MouseSquare equal to x and y divided by the Square Width to create a location for
// the user after clicking
private void setMouseSquare(int x, int y) {
//
xMouseSquare = x/squareWidth;
yMouseSquare = y/squareWidth;
}
/*
* Pre: SQUARES is an int that holds the number of game squares along one
* side of the game board. xSquare is an int such that 0 <= xSquare <
* SQUARES. CIRCLE_WIDTH is an int that holds the diameter of the circle to
* be drawn in the center of a square. squareWidth is an int that holds the
* width and height in pixels of a single game square.
*
* Post: Return the correct x-coordinate (in pixels) of the left side of the
* circle centered in a square in the column xSquare.
*
* Hint: This method should be very simple. What you need to do is to find
* the right equation.
*/
private int getIconDisplayXLocation(int xSquare) {
// This line is an example of using DEBUGGING variable
if (DEBUGGING) {
System.out.println("The input that getIconDisplayXLocation() receives is: " + xSquare);
}
// equation that returns the correct variable in the column xSquare
return squareWidth * xSquare + (squareWidth - CIRCLE_WIDTH)/2;
}
/*
* Pre: SQUARES is an int that holds the number of game squares along one
* side of the game board. ySquare is an int such that 0 <= ySquare <
* SQUARES. CIRCLE_WIDTH is an int that holds the diameter of the circle to
* be drawn in the center of a square. squareWidth is an int that holds the
* width and height in pixels of a single game square.
*
* Post: Return the correct y-coordinate (in pixels) of the top of the
* circle centered in a square in the row ySquare.
*
* Hint: This method should be very simple. What you need to do is to find
* the right equation.
*/
private int getIconDisplayYLocation(int ySquare) {
// This line is an example of using DEBUGGING variable
if (DEBUGGING) {
System.out.println("The input that getIconDisplayYLocation() receives is: " + ySquare);
}
// equation that returns the correct variable in the column ySquare
return squareWidth * ySquare + (squareWidth - CIRCLE_WIDTH)/2;
}
/*
* The instance variable gameBoard will be created and initialized
*
* Pre: SQUARES is set to an int. gameBoard is a 2-dimensional array type
* variable that holds the status of current game board. Each value in the
* array represents a square on the board
*
* Post: gameBoard must be assigned a new 2-dimensional array. Every square
* of gameBoard should be initialized to EMPTY.
*
* Hint: A loop.
*/
private void buildBoard() {
// Setting the two methods equal to local variables, x and y
int x = xMouseSquare;
int y = yMouseSquare;
// This line creates the gameBoard array. You must write several more
// lines to initialize all its values to EMPTY
// Write game board using the equation of three across and down for each column
// Constructs a 3 by 3 array of integers for the board
gameBoard = new int[3][3];
// Initialize variables i and j, set equal to 0; this establishes a connection for loop
for (x = 0; x < 3; x++) {
for (y = 0; y < 3; y++) {
gameBoard[x][y] = EMPTY;
}
}
}
/*
* Returns whether the most recently clicked square is a legal choice in the
* game.
*
* Pre: gameBoard is a 2-dimensional array type variable that holds the
* status of current game board. xSquare and ySquare represent the column
* and row of the most recently clicked square.
*
* Post: Returns true if and only if the square is a legal choice. If the
* square is empty on current game board, it is legal and the method shall
* return true; if it is taken by either human or CPU or it is not a square
* on current board, it is illegal and the method shall return false.
*
* Hint: Should be simple but think carefully to cover all cases.
*/
private boolean legalSquare(int xSquare, int ySquare) {
if (gameBoard[xSquare][ySquare] == EMPTY)
return true;
else return false;
}
/*
* Pre: gameBoard is an array that holds the current status of the game
* board. xSquare and ySquare represent the column and row of the most
* recently clicked square. player represent the current player (HUMAN or
* CPU). This method is always called after checking legalSquare().
*
* Post: Set the square as taken by current player on the game board if the
* square is empty.
*
* Hint: Very simple.
*/
private void setMovePosition(int xSquare, int ySquare, int player) {
player = HUMAN;
player = CPU;
this.xMouseSquare = xSquare;
this.yMouseSquare = ySquare;
}
/*
* Check if HUMAN or CPU wins the game.
*
* Pre: gameBoard is an array to hold square status values on the board. The
* status values can be EMPTY, HUMAN, CPU or other values.
*
* Post: The method will return true if and only if a player wins the game.
* Winning a game means there is at least one row, one column, or one
* diagonal that is taken by the same player. The method does not need to
* indicate who wins because if it is implemented correctly, the winner must
* be the one who just made a move.
*
* Hint: Complicated method. Use loops to shorten your code. Think about how
* to represent "a player takes 3 squares in a row/column/diagonal".
*/
private boolean gameEnd() {
// Setting local variables
int i = 0;
int x = xMouseSquare;
int y = yMouseSquare;
int sw = squareWidth;
int[][] g = gameBoard;
// Checking for a winner
// Checking columns
for (y = 0; y < 3; y++) {
for (x = 0; x < 3; x++) {
i = g[x][y] + i;
}
}
{
// Checking rows
for (x = 0; x < 3; x++) {
for (y = 0; y < 3; y++) {
i = g[x][y] + i;
}
}
}
// Checking first diagonal
{
for (x = 0; x < 3; x++) {
g[x][x] = 3;
}
}
for (x = 1; x < 3; x++) {
g[x][x] = 3;
}
for (x = 2; x < 3; x++) {
g[x][x] = 3;
}
// Checking second diagonal
for (y = 0; y < 3; y++) {
g[y][2 - y] = 3;
}
for (y = 1; y < 3; y++) {
g[y][2 - y] = 3;
}
for (y = 2; y < 3; y++) {
g[y][2 - y] = 3;
}
return true;
}
/*
* Check if the game ends as a draw.
*
* Pre: gameBoard is an array to hold square status values on the board. The
* status values can be EMPTY, HUMAN, CPU or other values. This method is
* always called after gameEnd().
*
* Post: The method will return true if and only if all squares on the game
* board are taken by HUMAN or CPU players (no EMPTY squares left).
*
* Hint: Should be simple. Use loops.
*/
// Value of...
private boolean gameDraw() {
if (squareWidth == (3^2 - 1)) {
}
return true;
}
/*
* Marks circles on the line on which a player wins.
*
* Pre: g is Graphics object that is ready to draw on. HUMAN_WINNING_COLOR
* and CPU_WINNING_COLOR are both Color objects that represent the color to
* show when HUMAN/CPU wins. gameBoard is gameBoard is an array to hold
* square status values on the board. The status values can be EMPTY, HUMAN,
* CPU or other values.
*
* Post: ALL the row(s)/column(s)/diagonal(s) on which a player wins will be
* marked as the special color.
*
* Hint: You must draw a new circle with the special color (to replace the
* old circle) on the square if the square belongs to a winning
* row/column/diagonal. You can change gameBoard because the board will be
* reset after displaying the winning line. You can use helper methods
* (existing ones or your own) to finish this method. Pay attention that
* many functions in this method is similar to gameEnd(). You should think
* about reusing code.
*
* Hint2: This method is not necessary for the game logic. You don't have to
* start it early. Start when you know everything else is correct.
*/
private void markWinningLine(Graphics g) {
// TODO
}
/*
* Generates the next square where the CPU plays a move.
*
* Pre: gameBoard is an array to hold square status values on
* the board. The status values can be EMPTY, HUMAN, CPU or other values.
*
* Post: Set xCPUMove and yCPUMove to represent the column and the row which
* CPU plans to play a move on. The respective square MUST BE EMPTY! It will
* cause a logical error if this method returns a square that is already
* taken!
*
* Hint: Don't start too early -- currently this method works (though
* naively). Make sure that everything else works before touching this
* method!
*/
private void CPUMove() {
// TODO
// The following block gives a naive solution -- it finds the first
// empty square and play a move on it. You can use this method to test
// other methods in the beginning. However, you must replace this block
// with your own algorithms eventually.
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
if (gameBoard[i][j] == 0) {
xCPUMove = i;
yCPUMove = j;
return;
}
}
}
}
/* Put any helper methods you wish to add here. */
/* You will not need to change anything below this line. */
/*
* DO NOT change this method.
*
* Set the game board to show a new, blank game.
*/
private void wipeBoard(Graphics g) {
g.setColor(BACKGROUND_COLOR);
g.fillRect(0, 0, BOARD_SIZE_PIXELS, BOARD_SIZE_PIXELS);
}
/*
* DO NOT change this method.
*
* Displays a circle on g, of the given color, in the center of the given
* square.
*/
private void displayHit(Graphics g, Color color, int xSquare, int ySquare) {
g.setColor(color);
g.fillOval(getIconDisplayXLocation(xSquare),
getIconDisplayYLocation(ySquare), CIRCLE_WIDTH, CIRCLE_WIDTH);
}
/*
* DO NOT change this method.
*
* This method handles mouse clicks. You will not need to call it.
*/
#Override
public boolean mouseDown(Event e, int xMouse, int yMouse) {
if (isClickable()) {
mouseClicked = true;
setMouseSquare(xMouse, yMouse);
}
repaint();
return true;
}
/*
* DO NOT change this method.
*
* This method handles drawing the board. You will not need to call it.
*/
#Override
public void update(Graphics g) {
paint(g);
}
/*
* DO NOT change this method.
*
* Draws the border between game squares onto canvas. Also, draws the moves
* that are already made.
*/
private void displayGame(Graphics canvas) {
canvas.setColor(SQUARE_BORDER_COLOR);
for (int i = 0; i < BOARD_SIZE_PIXELS; i += squareWidth) {
for (int j = 0; j < BOARD_SIZE_PIXELS; j += squareWidth) {
canvas.drawRect(i, j, squareWidth, squareWidth);
}
}
for (int i = 0; i < SQUARES; i++) {
for (int j = 0; j < SQUARES; j++) {
switch (gameBoard[i][j]) {
case HUMAN:
case HUMAN_WINNING:
displayHit(canvas, HUMAN_COLOR, i, j);
break;
case CPU:
case CPU_WINNING:
displayHit(canvas, CPU_COLOR, i, j);
break;
default:
break;
}
}
}
}
/*
* DO NOT change this method.
*
* This method relays information about the availability of mouse clicking
* in the game. You will not need to call it.
*/
private boolean isClickable() {
return !CPUinMove;
}
/*
* DO NOT change the contents this method.
*
* If this method is changed to public void paint(Graphics canvas), it will
* execute the program with the CPU-first order.
*
* This method is like the "main" method (but for applets). You will not
* need to call it. It contains most of the game logic.
*/
// #Override
public void paint(Graphics canvas) {
displayGame(canvas);
if (mouseClicked) {
if (onFirstMove) {
CPUMove();
setMovePosition(xCPUMove, yCPUMove, CPU);
displayHit(canvas, CPU_COLOR, xCPUMove, yCPUMove);
onFirstMove = false;
} else {
if (restart) {
wipeBoard(canvas);
setUpGame();
repaint();
} else if (legalSquare(xMouseSquare, yMouseSquare)) {
setMovePosition(xMouseSquare, yMouseSquare, HUMAN);
displayHit(canvas, HUMAN_COLOR, xMouseSquare, yMouseSquare);
if (gameEnd()) {
markWinningLine(canvas);
canvas.setFont(new Font("SansSerif", Font.PLAIN, 30));
canvas.setColor(GAME_OVER_MESSAGE_COLOR);
canvas.drawString(GAME_WIN_MESSAGE, squareWidth / 2,
squareWidth);
restart = true;
} else {
CPUinMove = true;
CPUMove();
setMovePosition(xCPUMove, yCPUMove, CPU);
displayHit(canvas, CPU_COLOR, xCPUMove, yCPUMove);
CPUinMove = false;
if (gameEnd()) {
markWinningLine(canvas);
canvas
.setFont(new Font("SansSerif", Font.PLAIN,
30));
canvas.setColor(GAME_OVER_MESSAGE_COLOR);
canvas.drawString(GAME_LOSE_MESSAGE,
squareWidth / 2, squareWidth);
restart = true;
} else if (gameDraw()) {
canvas
.setFont(new Font("SansSerif", Font.PLAIN,
30));
canvas.setColor(GAME_OVER_MESSAGE_COLOR);
canvas.drawString(GAME_DRAW_MESSAGE,
squareWidth / 2, squareWidth);
restart = true;
}
}
}
}
mouseClicked = false;
}
}
/*
* DO NOT change this method.
*
* This method is like the "main" method (but for applets). You will not
* need to call it. It contains most of the game logic.
*/
public void paint_game(Graphics canvas) {
// display the current game board
displayGame(canvas);
// the following block will run every time the user clicks the mouse
if (mouseClicked) { // when the user clicks the mouse
// if the game is ready to start or to be restarted
if (restart) {
// clear the window and set up the game again
wipeBoard(canvas);
setUpGame();
repaint();
}
// else, if the game is in play, check if the click is on a legal
// square
else if (legalSquare(xMouseSquare, yMouseSquare)) {
// if the square is legal, mark the corresponding position as
// taken by HUMAN
setMovePosition(xMouseSquare, yMouseSquare, HUMAN);
// display the new position with a HUMAN_COLOR circle
displayHit(canvas, HUMAN_COLOR, xMouseSquare, yMouseSquare);
// check if the game ends (if it is, HUMAN wins)
if (gameEnd()) {
// if HUMAN wins, mark the winning line as a special color.
markWinningLine(canvas);
// display the human winning message
canvas.setFont(new Font("SansSerif", Font.PLAIN, 30));
canvas.setColor(GAME_OVER_MESSAGE_COLOR);
canvas.drawString(GAME_WIN_MESSAGE, squareWidth / 2,
squareWidth);
// mark the game as ready to restart
restart = true;
} else if (gameDraw()) {
// if HUMAN doesn't win but the board is full, it is a draw
// display the draw message
canvas.setFont(new Font("SansSerif", Font.PLAIN, 30));
canvas.setColor(GAME_OVER_MESSAGE_COLOR);
canvas.drawString(GAME_DRAW_MESSAGE, squareWidth / 2,
squareWidth);
// mark the game as ready to restart
restart = true;
} else {
// if HUMAN doesn't win and the board is not full, the CPU
// is ready to move
CPUinMove = true;
// calculates the next CPU move
CPUMove();
// mark the corresponding position as taken by CPU
setMovePosition(xCPUMove, yCPUMove, CPU);
// display the new position with a CPU_COLOR circle
displayHit(canvas, CPU_COLOR, xCPUMove, yCPUMove);
CPUinMove = false;
if (gameEnd()) {
// if CPU wins, mark the winning line as a special
// color.
markWinningLine(canvas);
// display the human losing message
canvas.setFont(new Font("SansSerif", Font.PLAIN, 30));
canvas.setColor(GAME_OVER_MESSAGE_COLOR);
canvas.drawString(GAME_LOSE_MESSAGE, squareWidth / 2,
squareWidth);
// mark the game as ready to restart
restart = true;
}
// else (if the game is not ended after the CPU move), the
// game is ready to get the next HUMAN move
}
}
mouseClicked = false;
}
}
/*
* DO NOT change this method.
*
* This method initializes the applet. You will not need to call it.
*/
#Override
public void init() {
setSize(BOARD_SIZE_PIXELS, BOARD_SIZE_PIXELS);
setBackground(BACKGROUND_COLOR);
setUpGame();
}
/*
* DO NOT change this method.
*
* Creates a fresh game board and sets up the game state to get ready for a
* new game.
*/
private void setUpGame() {
buildBoard();
CPUinMove = false;
restart = false;
onFirstMove = true;
}
}
I've been given a 2^k * 2^k sized board, and one of the tiles is randomly removed making it a deficient board. The task is to fill the with "trominos" which are an L-shaped figure made of 3 tiles.
The process of the solving it isn't too difficult. If the board is 2x2, then it only takes one tromino to fill it. For any greater size, it must be divided into quarters (making four 2^(k-1) sized boards), with one tromino placed at the center point, so each quadrant has one filled in tile. After that, the board can be recursively filled until every tile is filled with a random colored tromino.
My main problem is actually implementing the code. My skills with Java programming are generally pretty weak, and I often have trouble simply finding a place to start. The only work to be done is in the tile method in the tiling class, which takes as input the deficient board to tile, the row and column to start tiling in, and the number of tiles to fill. This is a homework problem, so I'm simply looking for some guidance or a place to start - any help would be greatly appreciated.
public class BoardViewer extends JFrame {
private static final int WIDTH = 1024;
private static final int HEIGHT = 768;
private static final int OFFSET = 30;
public static final int UPVERT = 1;
public static final int DNVERT = 2;
public static final int RHORIZ = 4;
public static final int LHORIZ = 8;
public static final int FILL = 16;
private Color [][] board;
public BoardViewer(DeficientBoard db) {
super();
setSize(WIDTH + (OFFSET*2), HEIGHT + (OFFSET*2));
setDefaultCloseOperation(EXIT_ON_CLOSE);
setResizable(false);
board = db.getBoardColor();
}
public void paint(Graphics g) {
super.paint(g);
int width = WIDTH/board[0].length;
int height = HEIGHT/board.length;
for (int r = 0; r < board.length; r++)
for (int c = 0; c < board[r].length; c++) {
g.setColor(board[r][c]);
int x = c*width + OFFSET;
int y = r*height + OFFSET + (OFFSET/2);
g.fillRect(x+1, y+1, width-1, height-1);
}
}
}
public class DeficientBoard {
private int n;
private Color board[][];
// The four rotations of the tile.
// UL is XX
// X
// UR is XX
// X
// LL is X
// XX
// LR is X
// XX
public final int UL = 0;
public final int UR = 1;
public final int LL = 2;
public final int LR = 3;
/**
* Create a 2^k x 2^k deficient board.
*
* #param k power
*/
public DeficientBoard(int k) {
n = (int)Math.pow(2, k);
createBoard(Color.LIGHT_GRAY);
}
/**
* Actually create an n x n deficient board.
*
* #param color background color
*/
private void createBoard(Color color) {
board = new Color[n][n];
for (int r = 0; r < board.length; r++)
for (int c = 0; c < board[0].length; c++)
board[r][c] = color;
int d_row = (int)(Math.random() * n);
int d_col = (int)(Math.random() * n);
board[d_row][d_col] = Color.BLACK;
}
/**
* Given a row and column and shape based on that point
* place a tromino of the given color.
*
* #param r row
* #param c column
* #param s shape (UL, UR, LL, LR)
* #param theColor a Color
*/
public void placeTromino(int r, int c, int s, Color theColor) {
if (s == UL) {
board[r][c] = theColor;
board[r][c+1] = theColor;
board[r+1][c] = theColor;
} else if (s == UR) {
board[r][c] = theColor;
board[r][c+1] = theColor;
board[r+1][c+1] = theColor;
} else if (s == LL) {
board[r][c] = theColor;
board[r+1][c] = theColor;
board[r+1][c+1] = theColor;
} else {
board[r+1][c] = theColor;
board[r+1][c+1] = theColor;
board[r][c+1] = theColor;
}
}
/**
* Get the 2^k x 2^k board.
*
* #return the Color board.
*/
public Color[][] getBoardColor() {
return board;
}
/**
* Find and return the deficient row.
*
* #param row row
* #param col column
* #param sz size of the baord
* #return the row the deficient block is located
*/
public int getDeficientRow(int row, int col, int sz) {
for (int r = row; r < (row + sz); r++)
for (int c = col; c < (col + sz); c++)
if (board[r][c] != Color.LIGHT_GRAY)
return r;
return -1;
}
/**
* Find and return the deficient column.
*
* #param row row
* #param col column
* #param sz size of the baord
* #return the row the deficient block is located
*/
public int getDeficientCol(int row, int col, int sz) {
for (int r = row; r < (row + sz); r++)
for (int c = col; c < (col + sz); c++)
if (board[r][c] != Color.LIGHT_GRAY)
return c;
return -1;
}
/**
* Get the size of the deficient board.
*
* #return the size
*/
public int getSize() {
return n;
}
/**
* Display information about the deficient board.
*/
public String toString() {
return ("Deficient board of size "
+ n + "x" + n
+ " with position missing at ("
+ getDeficientRow(0, 0, n) + "," + getDeficientCol(0, 0, n) +").");
}
}
public class Tiling {
private static Color randColor() {
int r = (int)(Math.random() * 256);
int g = (int)(Math.random() * 256);
int b = (int)(Math.random() * 256);
return new Color(r, g, b);
}
public static void tile(DeficientBoard db, int row, int col, int n) {
}
public static void main(String[] args) {
DeficientBoard db = new DeficientBoard(3);
System.out.println(db);
tile(db, 0, 0, db.getSize());
BoardViewer bv = new BoardViewer(db);
bv.setVisible(true);
}
}
In general, when a recursive function implements a divide-and-conquer algorithm, it has to handle two basic cases:
The base case. This is the case where you're done dividing, and need to conquer a bit. In your assignment, the base case is the case where n = 2, and in that case, you just need to find which of the four tiles is missing/painted (using DefectiveBoard.getDeficientRow and DefectiveBoard.getDeficientCol) and add the appropriate triomino to cover the other three tiles.
The recursive case. This is the case where you're not done dividing, so you need to divide (i.e., recurse), and (depending on the algorithm) may need to do a bit of conquering either before or after the recursion. In your assignment, the recursive case is the case where n > 2. In that case, you need to do two things:
Find which of the four quadrants has a missing/painted tile, and add the appropriate triomino to cover one tile from each of the other three quadrants.
Recurse, calling yourself four times (one for each quadrant).
A good starting point is to write the "Is this the base case?" check, and to implement the base case.
After that, if you don't see how to write the recursive case, one approach is to temporarily write a "one above the base" case (n = 4), and see if you can generalize it. If not, you might then temporarily write a "two above the base" case (n = 8), and so on. (Once you've got your recursive algorithm working, you would then remove these special cases, since they're fully covered by the general recursive case.)
Well this is somewhat of a harder problem to solve. However, I'd say you have the skills given how much code you wrote so I wouldn't be self conscious about it.
I don't have a complete solution formulated, but I think if you start at the the removed tile and put a trominos on either side of it. Then keep putting trominos on either side of the last trominos. You're "spooning" the tromino you last placed on the board. Once you do that to the edge of the board. All that's left is tromino shaped locations. Here is an example of what I mean (X is the dropped tile ie the gap, Y are the trominos):
_ _ _ _
|_|_|_|_|
|_|Y|Y|_|
|_|Y|X|Y|
|_|_|Y|Y|
_ _ _ _
|Y|Y|_|_|
|Y|Y|Y|_|
|_|Y|X|Y|
|_|_|Y|Y|
Once the board is filled to the edges you can essentially start dropping trominos like bombs on the rest of the board. I have a feeling there is a pattern here where you fill in the diagonal trominos while filling in the 2nd part at the same time that is repeatable. But if you can't find that then create a recursive routine that spoons the gap to the edges then transitions to adding trominos in diagonal patterns. The hint there is you have to do the transition in the first stack frame only.
Good luck.
The code below will produce randomly generated circles of different colors. I need to be able to specify the color of the circle by it's size so that the loop will produce the same pattern of circles in different locations. This needs to be done with a 2d array. I know this is probably not that difficult, but I can't seem to grasp the concept.
Here are the directions and my code.
Thanks!
Set up a 2-D, int colors[][], array with 6 rows, one for each circle, and 3 columns, one for each element (Red, Green, Blue) of the colors to be used. In the above display the color values were randomly generated in the range 0 to 255 at the beginning of the program. Then for each diameter[i], color[i][0], color[i][1] and color[i][2] were used for the RGB levels.
import java.io.*;
import java.util.*;
import java.awt.*;
public class Lab10 {
/**
* #param args
*/
public static void main(String[] args) {
Scanner console=new Scanner (System.in);
Random r = new Random();
int [] color= new int [3];
color[0]=r.nextInt(256);
color[1]=r.nextInt(256);
color[2]=r.nextInt(256);
System.out.println("Please enter 6 integer values. The values should be in descending order and in the range 100 to 1.");
int[] diameters=new int[6];
int colors[][] = new int [6][3];
for(int i=0; i<6; i++){
diameters[i]=console.nextInt();//values entered
}
for (int i=0; i<diameters.length; i++) {
for (int j = 0; j < color.length; j++) {
colors[i][j]=colors[i][j];
}
}
int panelX = 400, panelY = 400;
DrawingPanel panel = new DrawingPanel(panelX, panelY);
panel.setBackground(Color.WHITE);
Graphics g = panel.getGraphics();
for (int i=0; i<10; i++){
int centerX=r.nextInt(350);
int centerY=r.nextInt(350);
for(int value:diameters){
g.setColor(new Color(r.nextInt(256),r.nextInt(256), r.nextInt(256)));
g.fillOval(centerX - value , centerY - value, 2 * value, 2 * value);
}
}
}
}
Hint
To break the concept for you.
int color[6][3] is your array.
To randomly generate the color values
create an object of class Random
Random random = new Random();
Do a loop on rows of color
for(int i=0;i<6;i++)
{
for(int j=0;j<3;j++)
color[i][j]= random.nextInt(255);
}
Now draw the circle like for diameter[i] ,
refer to color[i][0],color[i][1],color[i][2] for its rgb values
So if I got this right, you want to draw circles randomly, and you want the color to be dependent on diameter.
Here is my solution to that problem: pastebin
Also, i tried to show how it can be done both with and without java 8, so don't be confused about it.
Here is how it should look like: