What's a good Graph class to implement my logical game board? - java

I'm creating a board game on my computer using Java Swing.
Here's an image of the Swing GUI. I realize that it's hard to read the text of the board squares in this image. The requirement is that the player can move clockwise or counter-clockwise on the outside of the board, and through a stockholders meeting.
When it came time to create a logical model of the game board, I ran into problems.
Here's an enlarged image of the lower left of the game board.
The first logical model I tried was a List of my AbstractSquare class.
package com.ggl.stockmarket.game.model.board;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.Insets;
import java.awt.Polygon;
import java.awt.Rectangle;
import java.awt.font.FontRenderContext;
import java.awt.font.TextLayout;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.List;
import com.ggl.stockmarket.game.model.GameStatus;
import com.ggl.stockmarket.game.view.StockMarketFont;
public abstract class AbstractSquare {
public static final int IMAGE_WIDTH = 160;
public static final int IMAGE_HEIGHT = 192;
public static final Insets INSETS = new Insets(4, 2, 4, 2);
/**
* The key to the square in the bidirectional graph.
*/
protected Integer graphKey;
/**
* The direction to move on the next turn. +1 means clockwise, -1 means
* counter-clockwise, and zero means that an odd dice roll is clockwise, while
* an even dice roll is counter-clockwise.
*/
protected int direction;
/**
* The direction and distance to move the market. A positive integer means the
* market moves down. A negative integer means the market moves up.
*/
protected int marketAmount;
/**
* Pointer to the location in the List of the previous board square.
*/
protected int previousPointer;
/**
* Pointer to the locatio0n in the List of the next board square.
*/
protected int nextPointer;
/**
* The amount to multiply the stock. As an example; "1 for 1" multiplies the
* stock by 2.
*/
protected int stockMultiplier;
protected Color backgroundColor;
protected Dimension squareSize;
protected Rectangle boardLocation;
protected String multiplierText;
public Integer getGraphKey() {
return graphKey;
}
public void setGraphKey(Integer graphKey) {
this.graphKey = graphKey;
}
/**
* The direction to move on the next turn.
*
* #param roll - The dice roll total.
* #return +1 is clockwise and -1 is counter-clockwise.
*/
public int getDirection(int roll) {
if (direction == 0) {
if ((roll / 2 * 2) == roll) {
return -1;
} else {
return +1;
}
} else {
return direction;
}
}
public void setDirection(int direction) {
this.direction = direction;
}
public int getDirection() {
return direction;
}
public int getMarketAmount() {
return marketAmount;
}
public void setMarketAmount(int marketAmount) {
this.marketAmount = marketAmount;
}
public Color getBackgroundColor() {
return backgroundColor;
}
public void setBackgroundColor(Color backgroundColor) {
this.backgroundColor = backgroundColor;
}
public Rectangle getBoardLocation() {
return boardLocation;
}
public void setBoardLocation(Rectangle boardLocation) {
this.boardLocation = boardLocation;
}
public int getPreviousPointer() {
return previousPointer;
}
public void setPreviousPointer(int previousPointer) {
this.previousPointer = previousPointer;
}
public int getNextPointer() {
return nextPointer;
}
public void setNextPointer(int nextPointer) {
this.nextPointer = nextPointer;
}
public int getStockMultiplier() {
return stockMultiplier;
}
public void setStockMultiplier(int stockMultiplier) {
this.stockMultiplier = stockMultiplier;
}
public Dimension getSquareSize() {
return squareSize;
}
public void setSquareSize(Dimension squareSize) {
this.squareSize = squareSize;
}
public String getMultiplierText() {
return multiplierText;
}
public void setMultiplierText(String multiplierText) {
this.multiplierText = multiplierText;
}
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((graphKey == null) ? 0 : graphKey.hashCode());
return result;
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
AbstractSquare other = (AbstractSquare) obj;
if (graphKey == null) {
if (other.graphKey != null)
return false;
} else if (!graphKey.equals(other.graphKey))
return false;
return true;
}
public List<String> splitStockName(String name) {
List<String> list = new ArrayList<String>();
int pos = name.lastIndexOf(' ');
if (pos < 0) {
list.add(name);
} else {
list.add(name.substring(0, pos));
list.add(name.substring(pos + 1));
}
return list;
}
public BufferedImage drawOutsideImage() {
int width = squareSize.width;
int height = squareSize.height;
BufferedImage bufferedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
Graphics2D g = (Graphics2D) bufferedImage.getGraphics();
drawOutsideSquare(g, width, height, INSETS);
drawMovementText(g, width, height);
drawMovementArrows(g, width, height);
g.dispose();
return bufferedImage;
}
protected BufferedImage drawOutsideImage(int width, int height, Insets insets) {
BufferedImage bufferedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
Graphics2D g = (Graphics2D) bufferedImage.getGraphics();
drawOutsideSquare(g, width, height, insets);
drawMovementText(g, width, height);
drawMovementArrows(g, width, height);
return bufferedImage;
}
protected void drawOutsideSquare(Graphics2D g, int width, int height, Insets insets) {
g.setColor(Color.black);
g.fillRect(0, 0, width, height);
if (backgroundColor == null) {
g.setColor(Color.white);
} else {
g.setColor(backgroundColor);
}
g.fillRect(insets.left, insets.top, width - insets.right - insets.left, height - insets.bottom - insets.top);
}
protected void drawMovementText(Graphics2D g, int width, int height) {
Font directionFont = StockMarketFont.getBoldFont(16);
FontRenderContext frc = g.getFontRenderContext();
StringBuilder sb = new StringBuilder();
if (marketAmount < 0) {
sb.append("Down ");
sb.append(Math.abs(marketAmount));
} else if (marketAmount > 0) {
sb.append("Up ");
sb.append(marketAmount);
} else {
sb.append("Odd Even");
}
setTextColor(g);
TextLayout layout = new TextLayout(sb.toString(), directionFont, frc);
Rectangle2D bounds = layout.getBounds();
float fx = (float) (bounds.getX()) + (float) (width - bounds.getWidth()) * 0.5F;
float fy = (float) height - 10.0F;
layout.draw(g, fx, fy);
}
public void setTextColor(Graphics2D g) {
g.setColor(Color.black);
if ((backgroundColor != null) && (backgroundColor.equals(Color.blue))) {
g.setColor(Color.white);
}
}
protected void drawMovementArrows(Graphics2D g, int width, int height) {
if (direction == 0) {
int w = (width - 30) / 2;
int x = 10;
int y = height - 40;
drawArrow(g, x, y, +1, w);
x = width - w - 10;
drawArrow(g, x, y, -1, w);
} else {
int w = (width - 40);
int x = (width - w) / 2;
int y = height - 40;
drawArrow(g, x, y, direction, w);
}
}
protected void drawArrow(Graphics2D g, int x, int y, int direction, int length) {
// arrow thickness, arrow point height and width
int t = 4;
int h = 14;
int w = 20;
Polygon p = new Polygon();
if (direction > 0) {
g.fillRect(x + h, y, length - h, t);
p.addPoint(x + h, y - ((w - t) / 2));
p.addPoint(x + h, y + ((w + t) / 2));
p.addPoint(x, y + (t / 2));
} else {
g.fillRect(x, y, length - h, t);
p.addPoint(x + length - h, y - ((w - t) / 2));
p.addPoint(x + length - h, y + ((w + t) / 2));
p.addPoint(x + length, y + (t / 2));
}
g.fillPolygon(p);
}
public BufferedImage drawInsideImage(Insets insets) {
int width = squareSize.width;
int height = squareSize.height;
BufferedImage bufferedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
Graphics2D g = (Graphics2D) bufferedImage.getGraphics();
g.dispose();
return bufferedImage;
}
#Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append(graphKey);
return builder.toString();
}
public abstract void execute(GameStatus gameStatus);
}
For the moment, ignore the graphKey field. The AbstractSquare class contains all the fields needed for all of the sqaures on the game board. No square uses all of the fields. Each sqaure uses the fields that are needed for the sqaure.
The problem is that the board cannot be represented by a simple graph. As you can see on the enlarged game board, your move direction is determined by the arrow at the bottom of the square you start your turn on. You can move from the start square in either direction, depending on whether the dice roll was odd or even.
When you land on a stocholders meeting square during your roll, you have the choice whether to continue moving on the outside of the board or move into the stockholders meeting, assuming you own at least one share of that stock.
After I decided a List of AbstractSquares wasn't going to work, I investigated directed graphs. I found this code on Geeks For Geeks
// Java program to implement Graph
// with the help of Generics
import java.util.*;
class Graph<T> {
// We use Hashmap to store the edges in the graph
private Map<T, List<T> > map = new HashMap<>();
// This function adds a new vertex to the graph
public void addVertex(T s)
{
map.put(s, new LinkedList<T>());
}
// This function adds the edge
// between source to destination
public void addEdge(T source,
T destination,
boolean bidirectional)
{
if (!map.containsKey(source))
addVertex(source);
if (!map.containsKey(destination))
addVertex(destination);
map.get(source).add(destination);
if (bidirectional == true) {
map.get(destination).add(source);
}
}
// This function gives the count of vertices
public void getVertexCount()
{
System.out.println("The graph has "
+ map.keySet().size()
+ " vertex");
}
// This function gives the count of edges
public void getEdgesCount(boolean bidirection)
{
int count = 0;
for (T v : map.keySet()) {
count += map.get(v).size();
}
if (bidirection == true) {
count = count / 2;
}
System.out.println("The graph has "
+ count
+ " edges.");
}
// This function gives whether
// a vertex is present or not.
public void hasVertex(T s)
{
if (map.containsKey(s)) {
System.out.println("The graph contains "
+ s + " as a vertex.");
}
else {
System.out.println("The graph does not contain "
+ s + " as a vertex.");
}
}
// This function gives whether an edge is present or not.
public void hasEdge(T s, T d)
{
if (map.get(s).contains(d)) {
System.out.println("The graph has an edge between "
+ s + " and " + d + ".");
}
else {
System.out.println("The graph has no edge between "
+ s + " and " + d + ".");
}
}
// Prints the adjancency list of each vertex.
#Override
public String toString()
{
StringBuilder builder = new StringBuilder();
for (T v : map.keySet()) {
builder.append(v.toString() + ": ");
for (T w : map.get(v)) {
builder.append(w.toString() + " ");
}
builder.append("\n");
}
return (builder.toString());
}
}
// Driver Code
public class Main {
public static void main(String args[])
{
// Object of graph is created.
Graph<Integer> g = new Graph<Integer>();
// edges are added.
// Since the graph is bidirectional,
// so boolean bidirectional is passed as true.
g.addEdge(0, 1, true);
g.addEdge(0, 4, true);
g.addEdge(1, 2, true);
g.addEdge(1, 3, true);
g.addEdge(1, 4, true);
g.addEdge(2, 3, true);
g.addEdge(3, 4, true);
// print the graph.
System.out.println("Graph:\n"
+ g.toString());
// gives the no of vertices in the graph.
g.getVertexCount();
// gives the no of edges in the graph.
g.getEdgesCount(true);
// tells whether the edge is present or not.
g.hasEdge(3, 4);
// tells whether vertex is present or not
g.hasVertex(5);
}
}
The problem with this code is that my AbstractSqare instances had no natural key that I could use to create the graph of the game board.
My question is, how can I use a graph to implement the logical model of my game board?
I need to determine the intermediate squares of a dice roll so I can animate the movement of the piece. I also need to ask the player whether or not to enter a stockholders meeting, again assuming the player owns at least one share of the stock.

After a week of trial and error, I came up with a modified version of the Graph class from Geeks For Geeks.
package com.ggl.stockmarket.game.model;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
/**
* This class creates a directed or a bidirectional graph.
*
* #author Copied from the website GeeksForGeeks. Modified heavily by Gilbert Le
* Blanc
*
* #param <T> - Class to be modeled as a graph..
*/
public class Graph<T> {
// We use a List to store the objects
private List<T> list = new ArrayList<>();
// We use a Hashmap to store the key and edges in the graph
private Map<Integer, List<Integer>> map = new HashMap<>();
/**
* This method adds the class object and returns an Integer
* key to the class object.
* #param e - The class object to add.
* #return The Integer key to the class object.
*/
public Integer add(T e) {
list.add(e);
return list.size() - 1;
}
/**
* This method retrieves the Integer key for the class object.
*
* #param e - The class object to test.
* #return The Integer key to the class object, or -1 if the class object is not
* in the List.
*/
public Integer getKey(T e) {
for (int i = 0; i < list.size(); i++) {
T g = list.get(i);
if (g.equals(e)) {
return i;
}
}
return -1;
}
public T get(int key) {
return list.get(key);
}
public int size() {
return list.size();
}
/**
* This method adds the vertexes and edge between the Integer source and
* destination keys.
*
* #param source - Source key.
* #param destination - Destination key.
* #param bidirectional - True if the edge is bidirectio0nal, false if the edge
* is in one direction.
*
*/
public void addEdge(Integer source, Integer destination, boolean bidirectional) {
if (!map.containsKey(source)) {
addVertex(source);
}
if (!map.containsKey(destination)) {
addVertex(destination);
}
map.get(source).add(destination);
if (bidirectional == true) {
map.get(destination).add(source);
}
}
/**
* This method adds an Integer vertex to the graph.
*
* #param s - Key to add as a vertex.
*/
public void addVertex(Integer s) {
map.put(s, new LinkedList<>());
}
// This function gives the count of vertices
public String getVertexCount() {
return "The graph has " + map.keySet().size() + " vertex";
}
// This function gives the count of edges
public String getEdgesCount() {
int count = 0;
for (Integer v : map.keySet()) {
count += map.get(v).size();
}
return "The graph has " + count + " edges.";
}
// This method returns the number of edges for a vertex
public int getEdgeCount(Integer key) {
return map.get(key).size();
}
// This method returns the edges for a vertex
public List<Integer> getEdges(Integer key) {
return map.get(key);
}
// This function gives whether a vertex is present or not.
public String hasVertex(Integer s) {
if (map.containsKey(s)) {
return "The graph contains " + s + " as a vertex.";
} else {
return "The graph does not contain " + s + " as a vertex.";
}
}
// This function gives whether an edge is present or not.
public String hasEdge(Integer s, Integer d) {
if (map.get(s).contains(d)) {
return "The graph has an edge between " + s + " and " + d + ".";
} else {
return "The graph has no edge between " + s + " and " + d + ".";
}
}
// Prints the adjacency list of each vertex.
#Override
public String toString() {
StringBuilder builder = new StringBuilder();
for (Integer v : map.keySet()) {
builder.append(v.toString() + ": ");
for (Integer w : map.get(v)) {
builder.append(w.toString() + " ");
}
builder.append(System.lineSeparator());
}
return (builder.toString());
}
}
My inclination to store the AbstractSquare instances in an ArrayList turned out to be correct. What I was missing was a good way to store the edges and vertexes of the graph.
I stored each AbstractSquare in an ArrayList. As I added each AbstractSquare instace to the List, I saved the index of the instace as an edge. I then created vertices to connect the edges.
Here's a display of the first 13 square edges and their vertices. There are 120 logical squares on the board, so I'm not going to list all the vertices. I had to add the stockholders meeting squares to the logical model twice. Once to account for a clockwsie movement through the syockholders meeting and once to account for a counter-clockwise movement through the stockholders meeting.
0: 1 47
1: 0 2
2: 1 3
3: 2 4 111
4: 3 5
5: 4 6
6: 5 7
7: 6 8
8: 7 9
9: 8 10 48
10: 9 11
11: 10 12
12: 11 13
Square 0 can either move to square 47 or square 1. There are 48 squares along the outside of the game board. As you can see, a player in this part of the logical model can move in 2 or 3 directions.
Here's another part of the graph. This is one of the 8 stockholders meeting graphs.
111: 112
112: 113
113: 114
114: 115
115: 116
116: 117
117: 118
118: 119
119: 45
Once inside a stockholders meeting, the player can only move in one direction.
Here's a little bit of code from the factory class that builds the logical model of the board. The entire class is 862 lines. You can see how I save the edges and create the vertices of the graph.
// Position 0, starting at the bottom of the board,
// going clockwise;
StartSquare startSquare = createStartSquare();
Integer originKey = board.add(startSquare);
startSquare.setGraphKey(originKey);
startSquareGraphKey[0] = originKey;
BuySquare buySquare = createBuySquare(gameStatus, 3, +1, +1);
Integer key2 = board.add(buySquare);
board.addEdge(originKey, key2, true);
buySquare.setGraphKey(key2);
BuySquare buySquare2 = createBuySquare(gameStatus, 0, +1, -2);
Integer key = board.add(buySquare2);
board.addEdge(key2, key, true);
buySquare2.setGraphKey(key);
BuyLimitSquare buyLimitSquare = createBuyLimitSquare(gameStatus, 6, +1, +3, -1, 45);
key2 = board.add(buyLimitSquare);
board.addEdge(key, key2, true);
buyLimitSquare.setGraphKey(key2);
meetings[6] = buyLimitSquare;
Because I know I'm going to be asked, my logical model is separate from my view and my controller classes. I have each square draw itself, and I have classes that take each square image and locate them on the board image. I save the image rectangle of each square in the logical model, so I know where to draw the player pieces on the board image.
The point of this question and answer was to pass along my modified Graph class, in the hopes that it helps someone wrestling with a graph that's more complicated than the simple examples of graphs found in textbooks and online.

Related

How do i check, if two Circles are intersecting in Processing?

So I'm currently learning Java, and i struggle a lot to get my Code working.
I made a "Fun" Code in which some circles are popping up and it calculates how many of them are on the Upper Side of the Screen. (I know, some silly code.)
I'm coding it in the "Processing" Environment, Language is Java.
Here's my main File:
Circle[] circles = new Circle[50];
int index = 0;
boolean finished = false;
void setup() {
size(900, 900);
background(0);
for(int i = 0; i < circles.length; i++) {
circles[i] = new Circle();
}
if(finished = true) {
}
}
void draw() {
if(index < circles.length) {
circles[index].show(circles);
index++;
} else {
finished = true;
}
}
void count(Circle[] arr) {
int n = 0;
for(Circle c : arr) {
if(c.getY() > height / 2) {
n++;
}
}
println(n);
}
And here's the "Problem" Circle class:
class Circle {
private int x, y;
private float r = random(10, 25);
Circle() {
this.x = Math.round(random(0 + r, width - r));
this.y = Math.round(random(0 + r, height - r));
}
public int getX() {
return this.x;
}
public int getY() {
return this.y;
}
public void show(Circle[] arr) {
if(isColliding(arr)) {
this.x = Math.round(random(0 + r, width - r));
this.y = Math.round(random(0 + r, height - r));
} else {
ellipse(this.x, this.y, r * 2, r * 2);
stroke(255);
fill(255);
}
}
public boolean isColliding(Circle[] arr) {
boolean result = false;
if(arr == null) {
result = false;
} else {
for(Circle c : arr) {
float d = dist(c.getX(), c.getY(), this.x, this.y);
if(d < r * 2) {
result = true;
println("Collision at: " + c.getX() + " " + c.getY());
break;
}
}
}
return result;
}
}
As you can see, i already have a isColliding Method, and the Outputs in the Console seem to be right, however it won't work in the show() Method, the Circles won't stop intersecting each other.
So how can i make it work, that the Position is re-calculated when it is colliding?
Are you sure your collision method works? Unless I'm missing something, it should ALWAYS return true as you're passing in an array that includes itself.
That aside, I'd start looking at how your show() logic is laid out. You are checking for overlap, and then assigning a new random position if it finds any. This new position could very likely be on a circle that has already been drawn and in a good position.
Put your re-positioning in a loop so that it checks to make sure that it didn't just place itself onto an existing circle.
public void show(Circle[] arr)
{
/*
You could potentially get into a situation where you will NEVER find an empty spot.
Add an escape method for the loop.
*/
int failLimit = 500;
while(failLimit-- > 0 && isColliding(arr))
{
this.x = Math.round(random(0 + r, width - r));
this.y = Math.round(random(0 + r, height - r));
}
ellipse(this.x, this.y, r * 2, r * 2);
stroke(255);
fill(255);
}
You could simplify this and make it a bit more efficient by spawning in circles one at a time, checking to make sure their positions are good then.

Battleship Attack Method Java

I am well and truly stuck on this method. I need to create an attack method for a ComputerPlayer that chooses a random location and attacks the other player's board. Then, there is an attack method where I have to just attack the other player's board. However, when I run the program and place my guesses on my board (GUI), the dots just appear on the ComputerPlayer's board. I don't know why my methods are wrong though.
/**
* Attack the specified Location loc. Marks
* the attacked Location on the guess board
* with a positive number if the enemy Player
* controls a ship at the Location attacked;
* otherwise, if the enemy Player does not
* control a ship at the attacked Location,
* guess board is marked with a negative number.
*
* If the enemy Player controls a ship at the attacked
* Location, the ship must add the Location to its
* hits taken. Then, if the ship has been sunk, it
* is removed from the enemy Player's list of ships.
*
* Return true if the attack resulted in a ship sinking;
* false otherwise.
*
* #param enemy
* #param loc
* #return
*/
#Override
public boolean attack(Player enemy, Location loc)
{
int[][] array = getGuessBoard();
if(!enemy.hasShipAtLocation(loc))
array[loc.getRow()][loc.getCol()] = -1;
else
{
array[loc.getRow()][loc.getCol()] = 1;
enemy.getShip(loc).takeHit(loc);
}
if(enemy.getShip(loc).isSunk()) {
enemy.removeShip(enemy.getShip(loc));
return true;
}
return false;
}
#Override
public boolean attack(Player enemy, Location loc)
{
int range = (10 - 0) + 1;
int r = (int) Math.random() * range;
int c = (int) Math.random() * range;
int[][] array = getGuessBoard();
if(array[r][c] != -1)
{
if(!enemy.hasShipAtLocation(loc))
array[loc.getRow()][loc.getCol()] = -1;
else
{
array[loc.getRow()][loc.getRow()] = 1;
enemy.getShip(loc).takeHit(loc);
}
if(getShip(loc).isSunk())
{
enemy.removeShip(enemy.getShip(loc));
return true;
}
}
return false;
}
GUI for Game
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
public class BattleshipDriver extends Canvas implements MouseListener
{
public static final int WIDTH = 1024, HEIGHT = WIDTH / 12 * 9;
private Battleship battleship;
private int x, y, squareSize, len;
private Player p1, p2;
private BufferedImage logo, end;
public BattleshipDriver()
{
battleship = new Battleship();
battleship.addPlayer(new HumanPlayer("Mr. Hubbard"));
battleship.addPlayer(new ComputerPlayer("AlphaBattleship"));
x = 90;
y = 200;
squareSize = 36;
len = squareSize * 10 - 1;
p1 = battleship.getPlayer(0);
p2 = battleship.getPlayer(1);
// Get Battleship Logo
try {
logo = ImageIO.read(new File("src/Logo.png"));
} catch (IOException e) {
e.printStackTrace();
}
// Get End Screen
try {
end = ImageIO.read(new File("src/End.png"));
} catch (IOException e) {
e.printStackTrace();
}
addMouseListener(this);
new Window(WIDTH, HEIGHT, "Battleship", this);
try {
Thread.sleep(100);
} catch(InterruptedException ex) {
Thread.currentThread().interrupt();
}
render();
}
private void render()
{
Graphics g = getGraphics();
// Background
g.setColor(Color.DARK_GRAY);
g.fillRect(0, 0, WIDTH, HEIGHT);
if(!battleship.gameOver())
{
// Boards
renderGrid(g, x, y, squareSize);
renderGuesses(g, p1, x, y, squareSize);
renderGrid(g, 570, y, squareSize);
renderGuesses(g, p2, 570, y, squareSize);
// Names
g.setColor(Color.WHITE);
g.drawString(p1.getName(), x, y + 25 + len);
g.drawString(p2.getName(), 570, y + 25 + len);
}
else
{
// End Screen
g.drawImage(end, 0, 0, this);
g.setColor(Color.WHITE);
g.setFont(new Font("Arial", 1, squareSize));
String winner = battleship.getWinner().getName();
g.drawString(winner, WIDTH / 2 - (winner.length() * squareSize / 4), HEIGHT / 4);
g.drawString("Wins!", WIDTH / 2 - ("Wins!".length() * squareSize / 4), HEIGHT / 4 + squareSize);
}
// Battleship Logo
g.drawImage(logo, WIDTH / 2 - 246, 10, this);
g.dispose();
}
private void renderGrid(Graphics g, int x, int y, int s)
{
g.setColor(Color.WHITE);
g.setFont(new Font("Arial", 1, s / 2));
// Row Lines
for(int i = 0; i < 11; i++)
g.drawLine(x, y+i*s, x+len, y+i*s);
// Column Lines
for(int i = 0; i < 11; i++)
g.drawLine(x+i*s, y, x+i*s, y+len);
// Row Markers
for(int i = 0; i < 10; i++) //marks row coordinates on side
g.drawString(i + "", x-(int)(s*0.43), y+(int)(s*0.67)+s*i);
// Column Markers
for(int i = 0; i < 10; i++) //marks column coordinates on top
g.drawString(i + "", x+(int)(s*0.4)+s*i, y-(int)(s*0.2));
}
public void renderGuesses(Graphics g, Player player, int x, int y, int s)
{
int[][] guessBoard = player.getGuessBoard();
for(int r = 0; r < guessBoard.length; r++)
for(int c = 0; c < guessBoard[r].length; c++)
if(guessBoard[r][c] > 0) // hit
{
g.setColor(Color.RED);
g.fillOval(c*s+x+(int)(s*0.35), r*s+y+(int)(s*0.35), (int)(s*0.33), (int)(s*0.33));
}
else if(guessBoard[r][c] < 0) // miss
{
g.setColor(Color.WHITE);
g.fillOval(c*s+x+(int)(s*0.35), r*s+y+(int)(s*0.35), (int)(s*0.33), (int)(s*0.33));
}
}
#Override
public void mouseClicked(MouseEvent e)
{
int r = e.getY();
int c = e.getX();
int len = squareSize * 10 - 1;
if(r > y && r < y + len && c > x && c < x + len) // clicked on board
{
int row = (r - y) / squareSize;
int col = (c - x) / squareSize;
System.out.println(row + ", " + col);
Location loc = new Location(row, col);
if(p1.getGuessBoard()[row][col] == 0)
{
p1.attack(p2, loc);
p2.attack(p1, loc);
}
battleship.upkeep();
render();
}
System.out.println(r + ", " + c);
}
#Override
public void mousePressed(MouseEvent e) {}
#Override
public void mouseReleased(MouseEvent e) {}
#Override
public void mouseEntered(MouseEvent e) {}
#Override
public void mouseExited(MouseEvent e) {}
public static void main(String[] args)
{
new BattleshipDriver();
}
}

Recursion (done)

I wrote this path finding algorithm that should be working, but I'm getting tons of java.lang.ArrayIndexOutOfBoundsException. The goal of the program is to find the shortest path from one point to another that costs the least. Here's my code:
public boolean travel(int[][] path, int cX, int cY, int eX, int eY)
{
boolean returned = false;
System.out.println("the current X position on the GRID is: "+cX+"the current y position on the GRID is: "+cY);
path[cX][cY]=1;
if(cost>lowestCost - grid[cX][cY]){
return false;
}
cost += grid[cX][cY];
if(cX>=eX && cY>=eY){
return true;
}
if(cX+1>=eX && cY+1<eY){
return false;
}
if(cY+1>=eY && cX+1<eX){
return false;
}
if(travel(path,cX+1,cY+1,eX,eY)==true){
returned=true;
replace(newBest, path);
}
if(travel(path,cX,cY+1,eX,eY)==true){
returned=true;
replace(newBest, path);
}
if(travel(path,cX+1,cY,eX,eY)==true){
returned=true;
replace(newBest, path);
}
return(returned);
}
cX is the current X position in the array, cY is the current Y position in the array, eX and eY are the destination coordinates. path[][] is the array used to store the path. If you have any answers please tell me! also don't suggest any other algorithms, just some edits to the actual code. grid[][] is the array that stores the cost to go from one to another. Thanks very much
if(travel(newBest,0,0,rows,columns)==true)
{
lowestCost=cost;
}
This is how I call the method to find the shortest path.
This is the entire applet:
import java.applet.*;
import java.awt.*;
import java.awt.event.*;
import java.util.*;
public class GridWorld extends Applet implements Runnable, MouseListener, KeyListener, MouseMotionListener
{
public int worldx;
public int worldy;
public int columns;
public int rows;
public int destX, destY;
public int cost, lowestCost;
public boolean sizeD;
public int[][] grid;
public int[][] prevBest;
public int[][] newBest;
Graphics bufferGraphics; //Set up double buffer
Image offscreen;
Thread thread;//Sets up a Thread called thread
public void init()
{
worldx=1000;
worldy=1000;
cost=0;
lowestCost=5000;
sizeD=false;
columns=5;
rows=5;
destX=0;
destY=0;
grid= new int[rows][columns];
prevBest= new int[rows][columns];
newBest = new int[rows][columns];
offscreen = createImage(worldx,worldy); //create a new image that's the size of the applet DOUBLE BUFFER SET UP
bufferGraphics = offscreen.getGraphics(); //set bufferGraphics to the graphics of the offscreen image. DOUBLE BUFFER SET UP
addKeyListener(this);//setup all the listeners
addMouseListener(this);//setup all the listeners
addMouseMotionListener(this);//setup all the listeners
thread = new Thread(this); //constructs a new thread
thread.start(); //starts the thread
}//init()
public void fillGrid()
{
prevBest= new int[rows][columns];
newBest = new int[rows][columns];
lowestCost = 0;
for(int ro = 0;ro<rows;ro++)
{
for(int col = 0;col<columns;col++)
{
grid[ro][col]=(int)(Math.random()*100);
newBest[ro][col]=0;
prevBest[ro][col]=0;
if(ro==col)
{
prevBest[ro][col]=1;
lowestCost+=grid[ro][col];
}
}
}
destX=(rows-1);
destY=(columns-1);
}
public boolean baseCase(int ct, int lowct, int destR, int destC, int cX, int cY)
{
boolean returned=false;
if(ct>=lowct)
{
returned=true;
}
if(cX+1==rows)
{
returned=true;
}
if(cY+1==columns)
{
returned=true;
}
if(cX==destR && cY==destC)
{
returned=true;
}
return(returned);
}
public boolean isValid(int x, int y, int[][] path, int eX, int eY) {
//not valid if: cordinates are into grid dimensions
if (!((x >= 0 && x < grid.length) && (y >= 0 && y < grid.length)))
return false;
//valid if: not visited yet, or is destiny
if (path[x][y] == 0 || (x == eX && y == eY))
return true;
return true;
}
/*public int traverse(int steps, int destR, int destC, int curX, int curY)
{
int direction = 0;
if(cost>=lowestCost)
{
//System.out.println("Greater cost Base Case");
direction=4;
}
if(curX+1>=destR && curY+1<destC)
{
System.out.println("Reached the farthest row Base Case");
direction=1;
}
if(curY+1>=destC && curY+1<destR)
{
System.out.println("Reached the farthest columns Base Case");
direction=2;
}
if(curX+1>=destR && curY+1>=destC)
{
System.out.println("At destination Base Case");
direction=4;
}
switch(direction)
{
case 0: newBest[curX][curY]=1;
cost+=grid[curX][curY];
System.out.println("the current X position on the GRID is: "+curX+"the current y position on the GRID is: "+curY);
return(traverse(steps+1,destR,destC,curX+1,curY+1)); //diag
case 1: newBest[curX][curY]=1;
cost+=grid[curX][curY];
return(traverse(steps+1,destR,destC,curX,curY+1)); //right
case 2: newBest[curX][curY]=1;
cost+=grid[curX][curY];
return(traverse(steps+1,destR,destC,curX+1,curY));//down
case 3:
return(5000);
case 4: System.out.println("the Grid's cost is: "+cost);
return(cost);
default: return(0);
}
}*/
public int[][] replace(int[][] p1, int[][] p2)
{
for(int col=0;col<columns;col++)
{
for(int ro=0;ro<rows;ro++)
{
p1[ro][col]=p2[ro][col];
}
}
return(p1);
}
public boolean travel(int[][] path, int cX, int cY, int eX, int eY)
{
boolean returned = false;
System.out.println("cX: "+ cX+" , cY: "+ cY+", eX: "+eX+", eY: " +eY+" Path 1 length: "+path[0].length+" Path 2 length: "+path[1].length);
path[cX][cY]=1;
if(cost>lowestCost - grid[cX][cY]){
System.out.println("1");
return false;
}
cost += grid[cX][cY];
}
if(travel(path,cX+1,cY+1,eX,eY)==true && isValid(cX+1,cY+1,newBest,eX,eY)){
System.out.println("the current X position on the GRID is: "+cX+"the current y position on the GRID is: "+cY);
returned=true;
replace(newBest, path);
}
if(travel(path,cX,cY+1,eX,eY)==true && isValid(cX,cY+1,newBest,eX,eY)){
System.out.println("the current X position on the GRID is: "+cX+"the current y position on the GRID is: "+cY);
returned=true;
replace(newBest, path);
}
if(travel(path,cX+1,cY,eX,eY)==true && isValid(cX+1,cY,newBest,eX,eY)){
System.out.println("the current X position on the GRID is: "+cX+"the current y position on the GRID is: "+cY);
returned=true;
replace(newBest, path);
}
return(returned);
}
public void paint(Graphics g)
{// paint() is used to display things on the screen
setSize(worldx,worldy);
//clear the offscreen image
bufferGraphics.clearRect(0,0,worldx,worldy);
bufferGraphics.setColor(Color.black);
//bufferGraphics.fillRect(0,0,worldx,worldy);
if(sizeD==true)
{
if(travel(newBest,0,0,rows,columns)==true)
{
lowestCost=cost;
}
}
for(int ro = 0;ro<rows;ro++)
{
for(int col = 0;col<columns;col++)
{
if(sizeD==true)
{
if(newBest[ro][col]==1)
{
bufferGraphics.setColor(Color.red);
bufferGraphics.fillRect((50*col),(50*ro),50,50);
bufferGraphics.setColor(Color.black);
}
if(prevBest[ro][col]==1)
{
bufferGraphics.setColor(Color.gray);
bufferGraphics.fillRect((50*col),(50*ro),50,50);
bufferGraphics.setColor(Color.black);
}
bufferGraphics.drawLine(0,(50*ro),50*columns,(50*ro));
bufferGraphics.drawLine((50*col),0,(50*col),50*rows);
bufferGraphics.drawString(""+grid[ro][col],(50*ro)+25,(50*col)+25);
}
}
}
if(sizeD==false)
{
bufferGraphics.drawRect(200,300,100,100);
bufferGraphics.drawString("5",250,350);
bufferGraphics.drawRect(400,300,100,100);
bufferGraphics.drawString("10",450,350);
bufferGraphics.drawRect(600,300,100,100);
bufferGraphics.drawString("20",650,350);
}
g.drawImage(offscreen,0,0,worldx,worldy,this);//Draw the screen
}// paint()
public void mouseDragged(MouseEvent e) {
}
public void mouseMoved(MouseEvent e){
}
public void mousePressed(MouseEvent e)
{
}
public void mouseReleased(MouseEvent e)
{
}
public void mouseEntered(MouseEvent e)
{
System.out.println("Mouse entered");
}
public void mouseExited(MouseEvent e)
{
System.out.println("Mouse exited");
}
public void mouseClicked(MouseEvent e)
{
System.out.println("Mouse clicked (# of clicks: "+ e.getClickCount() + ")");
int mX=e.getX();
int mY=e.getY();
if(new Rectangle(200,300,100,100).contains(mX,mY) && sizeD==false)
{
columns=5;
rows=5;
grid= new int[rows][columns];
fillGrid();
sizeD=true;
}
if(new Rectangle(400,300,100,100).contains(mX,mY) && sizeD==false)
{
columns=10;
rows=10;
grid= new int[rows][columns];
fillGrid();
sizeD=true;
}
if(new Rectangle(600,300,100,100).contains(mX,mY) && sizeD==false)
{
columns=20;
rows=20;
grid= new int[rows][columns];
fillGrid();
sizeD=true;
}
}
public void keyPressed( KeyEvent event )
{
String keyin; // define a non‐public variable to hold the string representing the key input
keyin = ""+event.getKeyText( event.getKeyCode());
System.out.println("Key pressed "+keyin);
}//keyPressed()
public void keyReleased( KeyEvent event )
{
String keyin;
keyin = ""+event.getKeyText( event.getKeyCode());
System.out.println ("Key released: "+ keyin);
}//keyReleased()
public void keyTyped( KeyEvent event )
{
char keyin;
keyin = event.getKeyChar(); //getKeyChar() returns the character of the printable key pressed.
System.out.println ("Key Typed: "+ keyin);
}//keyTyped()
public void update (Graphics g)
{
paint(g);
}//Update the graphics
public void run()
{
while(true) // this thread loop forever and runs the paint method and then sleeps.
{
repaint();
try {
thread.sleep(50);
}
catch (Exception e){ }
}//while
}// run()
}//Applet
You are getting tons of java.lang.ArrayIndexOutOfBoundsException because you didn't proper manage the flow to enter the last three if-block. The code will enter the last three if-block since you only return the state at return(returned); even you have set boundary check (first two if-block). So, path[cX][cY] and grid[cX][cY] may encounter index out of bound when cX and cY is a large value (depends on the index setup of path and grid).
Also, the checking logic for first four if-block is not in correct order and you should return state when the condition meet.
The first four if-block should rearrange as:
if(cost>=lowestCost){
return false;
}
if(cX==eX && cY==eY){
return true;
}
if(cX+1>=eX && cY+1<eY){
return false;
}
if(cY+1>=eY && cX+1<eX){
return false;
}
BTW, please ensure your boundary checking (cX+1>=eX && cY+1<eY and cY+1>=eY && cX+1<eX) is correct. This will makes the code cannot access points in ([eX-1,eX],[0,eY-2]) and ([0,eX-2],[eY-1,eY]).
One more point, you might get trouble for cost>=lowestCost for rare cases like all possible shortest path has cost equal to pre-set value of lowestCost. One way to handle this is remove equal sign.
One more again, you might get trouble for cost>=lowestCost for extreme cases like cost = Integer.MAX_VALUE + 1. To handle this, you may try
if(cost>lowestCost - grid[cX][cY]){
return false;
}
cost += grid[cX][cY];
Here is a recursive solution with backtraking, code is explained:
public class FindShortestPath {
//the map is represented as a graph, and the graph is represented as an adyacent matrix
int map[][] = {{-1, 1, 1, 20}, //-1 = there is no edge between two vertexs
{-1, -1, 3, 1},
{-1, -1, -1, 1},
{-1, -1, -1, -1}};
int distSol = Integer.MAX_VALUE; //we want to minimize this value
List<Integer> solution = new ArrayList<Integer>(); //path solution
public static void main(String[] args) {
new FindShortestPath().start();
}
void start() {
/* In this case we find shortest path from 0 to 3 */
findShortestPath(new boolean[map.length], 0, 3, 0, new ArrayList<Integer>());
System.out.println("Distance: " + distSol);
System.out.println(solution.toString());
}
/**
*
* #param visit auxiliary array to mark visited
* #param c actual vertex
* #param destiny destiny vertex
* #param distAct actual distance traveled
* #param path actual path traveled
*/
void findShortestPath(boolean visit[], int c, int destiny, int distAct, List<Integer> path) {
if (c == destiny) { //base case: we reach destiny
if (distAct <= distSol) { //check if actual distance is better than solution
distSol = distAct;
solution = new ArrayList<Integer>(path); //replace old solution
solution.add(destiny);
}
} else { //recursive case
path.add(c); //add actual vertex as a possible candidate into actual path
visit[c] = true;
for (int i = 0; i < map.length; i++) { //visit every adyacent vertex that was no visited yet
if (map[c][i] != -1 && !visit[i]) {
findShortestPath(visit, i, destiny, distAct + map[c][i], path);
}
}
path.remove(path.size() - 1); //remove this vertex from the path
visit[c] = false;
}
}
}
Output for this example:
Distance: 2
[0, 2, 3]
EDIT
The OP wants the map to be a grid:
public class FindShortestPath2 {
int lowestCost = Integer.MAX_VALUE;
int grid[][] = {{0, 9, 0, 0},
{ 0, 5, 1, 1},
{ 0, 0, 2, 0},
{ 0, 0, 0, 0}};
int sol[][] = new int[4][4]; //this is the path solution
public static void main(String[] args) {
new FindShortestPath2().start();
}
void start() {
travel(new int[4][4], 0, 0, 2, 2, 0);
System.out.println("Lowest cost: " + lowestCost);
printPath(sol); //print solution
}
public void travel(int[][] path, int cX, int cY, int eX, int eY, int cost) {
if (cX == eX && cY == eY) { //reach destiny cordinates
if (cost < lowestCost) {
lowestCost = cost;
path[cX][cY] = 1;
replace(sol, path);
}
} else {
path[cX][cY] = 1; //mark path
if (isValid(cX + 1, cY + 1, path, eX, eY)) { //move in diagonal (rigth-down)
travel(path, cX + 1, cY + 1, eX, eY, cost + grid[cX + 1][cY+1]);
}
if (isValid(cX, cY + 1, path, eX, eY)) { //move rigth
travel(path, cX, cY + 1, eX, eY, cost + grid[cX][cY+1]);
}
if (isValid(cX + 1, cY, path, eX, eY)) { //move down
travel(path, cX + 1, cY, eX, eY, cost + grid[cX + 1][cY]);
}
path[cX][cY] = 0; //unmark path
}
}
boolean isValid(int x, int y, int[][] path, int eX, int eY) {
//not valid if: cordinates are into grid dimensions
if (!((x >= 0 && x < grid.length) && (y >= 0 && y < grid.length)))
return false;
//valid if: not visited yet, or is destiny
if (path[x][y] == 0 || (x == eX && y == eY))
return true;
return true;
}
void replace(int[][] p1, int[][] p2) {
for (int col = 0; col < p1.length; col++) {
for (int ro = 0; ro < p1.length; ro++) {
p1[ro][col] = p2[ro][col];
}
}
}
void printPath(int[][] p) {
for (int col = 0; col < p.length; col++) {
for (int ro = 0; ro < p.length; ro++) {
System.out.print(p[col][ro] + " ");
}
System.out.println();
}
System.out.println();
}
}
Output for this example:
Lowest cost: 2
1 0 0 0
1 0 0 0
0 1 1 0
0 0 0 0
This may be off topic, but have you tried implementing Dijkstra's Algorithm in Java. In the following algorithm, the code u := vertex in Q with min dist[u], searches for the vertex u in the vertex set Q that has the least dist[u] value. length(u, v) returns the length of the edge joining (i.e. the distance between) the two neighbor-nodes u and v. The variable alt on line 17 is the length of the path from the root node to the neighbor node v if it were to go through u. If this path is shorter than the current shortest path recorded for v, that current path is replaced with this alt path. The previous array is populated with a pointer to the "next-hop" node on the source graph to get the shortest route to the source. (taken from Wikipedia's artice on Dijkstra's Algorithm. This work is not my own.
function Dijkstra(Graph, source):
dist[source] := 0 // Distance from source to source
for each vertex v in Graph: // Initializations
if v ≠ source
dist[v] := infinity // Unknown distance function from source to v
previous[v] := undefined // Previous node in optimal path from source
end if
add v to Q // All nodes initially in Q (unvisited nodes)
end for
while Q is not empty: // The main loop
u := vertex in Q with min dist[u] // Source node in first case
remove u from Q
for each neighbor v of u: // where v has not yet been removed from Q.
alt := dist[u] + length(u, v)
if alt < dist[v]: // A shorter path to v has been found
dist[v] := alt
previous[v] := u
end if
end for
end while
return dist[], previous[]
end function

How to Rotate Circle with text on Canvas in Blackberry

How to Rotate Circle with Text on TouchEvent or on TrackBallMoveEvent.
How do I create this kind of circle?
I had created a circle and rotated it also, but it always starts from 0 degrees.
Is there any other option to create this kind of circle?
Each circle have different text and each of the circles can move independently.
So, this is definitely not complete, but I think it's most of what you need.
Limitations/Assumptions
I have so far only implemented touch handling, as I think that's more difficult. If I get time later, I'll come back and add trackball handling.
I did not give the spinning discs any momentum. After the user's finger leaves the disc, it stops spinning.
I'm not sure the focus transitions between discs are 100% right. You'll have to do some testing. They're mostly right, at least.
When you mentioned Canvas in the title, I assumed that didn't mean you required this to utilize the J2ME Canvas. Writing BlackBerry apps with the RIM UI libraries is pretty much all I've done.
Solution
Essentially, I created a Field subclass to represent each disc. You create the field by passing in an array of labels, to be spaced around the perimeter, a radius, and a color. Hardcoded in each DiscField is an edge inset for the text, which kind of assumes a certain size difference between discs. You should probably make that more dynamic.
public class DiscField extends Field {
/** Used to map Manager's TouchEvents into our coordinate system */
private int _offset = 0;
private int _radius;
private int _fillColor;
private double _currentRotation = 0.0;
private double _lastTouchAngle = 0.0;
private boolean _rotating = false;
private String[] _labels;
/** Text inset from outer disc edge */
private static final int INSET = 30;
private DiscField() {
}
public DiscField(String[] labels, int radius, int fillColor) {
super(Field.FOCUSABLE);
_labels = labels;
_radius = radius;
_fillColor = fillColor;
}
protected void layout(int width, int height) {
setExtent(Math.min(width, getPreferredWidth()), Math.min(height, getPreferredHeight()));
}
private void drawFilledCircle(Graphics g, int x, int y, int r) {
// http://stackoverflow.com/a/1186851/119114
g.fillEllipse(x, y, x + r, y, x, y + r, 0, 360);
}
private void drawCircle(Graphics g, int x, int y, int r) {
g.drawEllipse(x, y, x + r, y, x, y + r, 0, 360);
}
protected void paint(Graphics graphics) {
int oldColor = graphics.getColor();
graphics.setColor(_fillColor);
drawFilledCircle(graphics, _radius, _radius, _radius);
graphics.setColor(Color.WHITE);
drawCircle(graphics, _radius, _radius, _radius);
// plot the text around the circle, inset by some 'padding' value
int textColor = (_fillColor == Color.WHITE) ? Color.BLACK : Color.WHITE;
graphics.setColor(textColor);
// equally space the labels around the disc
double interval = (2.0 * Math.PI / _labels.length);
for (int i = 0; i < _labels.length; i++) {
// account for font size when plotting text
int fontOffsetX = getFont().getAdvance(_labels[i]) / 2;
int fontOffsetY = getFont().getHeight() / 2;
int x = _radius + (int) ((_radius - INSET) * Math.cos(i * interval - _currentRotation)) - fontOffsetX;
int y = _radius - (int) ((_radius - INSET) * Math.sin(i * interval - _currentRotation)) - fontOffsetY;
graphics.drawText(_labels[i], x, y);
}
graphics.setColor(oldColor);
}
protected void drawFocus(Graphics graphics, boolean on) {
if (on) {
int oldColor = graphics.getColor();
int oldAlpha = graphics.getGlobalAlpha();
// just draw a white shine to indicate focus
graphics.setColor(Color.WHITE);
graphics.setGlobalAlpha(80);
drawFilledCircle(graphics, _radius, _radius, _radius);
// reset graphics context
graphics.setColor(oldColor);
graphics.setGlobalAlpha(oldAlpha);
}
}
protected void onUnfocus() {
super.onUnfocus();
_rotating = false;
}
protected boolean touchEvent(TouchEvent event) {
switch (event.getEvent()) {
case TouchEvent.MOVE: {
setFocus();
// Get the touch location, within this Field
int x = event.getX(1) - _offset - _radius;
int y = event.getY(1) - _offset - _radius;
if (x * x + y * y <= _radius * _radius) {
double angle = MathUtilities.atan2(y, x);
if (_rotating) {
// _lastTouchAngle only valid if _rotating
_currentRotation += angle - _lastTouchAngle;
// force a redraw (paint) with the new rotation angle
invalidate();
} else {
_rotating = true;
}
_lastTouchAngle = angle;
return true;
}
}
case TouchEvent.UNCLICK:
case TouchEvent.UP: {
_rotating = false;
return true;
}
case TouchEvent.DOWN: {
setFocus();
int x = event.getX(1) - _offset - _radius;
int y = event.getY(1) - _offset - _radius;
if (x * x + y * y <= _radius * _radius) {
_lastTouchAngle = MathUtilities.atan2(y, x);
_rotating = true;
return true;
}
}
default:
break;
}
return super.touchEvent(event);
}
protected boolean trackwheelRoll(int arg0, int arg1, int arg2) {
return super.trackwheelRoll(arg0, arg1, arg2);
// TODO!
}
public int getPreferredHeight() {
return getPreferredWidth();
}
public int getPreferredWidth() {
return 2 * _radius;
}
public String[] getLabels() {
return _labels;
}
public void setLabels(String[] labels) {
this._labels = labels;
}
public int getRadius() {
return _radius;
}
public void setRadius(int radius) {
this._radius = radius;
}
public double getCurrentAngle() {
return _currentRotation;
}
public void setCurrentAngle(double angle) {
this._currentRotation = angle;
}
public int getOffset() {
return _offset;
}
public void setOffset(int offset) {
this._offset = offset;
}
}
Containing all the DiscField objects is the DiscManager. It aligns the child DiscFields in sublayout(), and handles proper delegation of touch events ... since the fields overlap, and a touch within a DiscFields extent that does not also fall within its radius (i.e. the corners) should be handled by a larger disc.
/**
* A DiscManager is a container for DiscFields and manages proper delegation
* of touch event handling.
*/
private class DiscManager extends Manager {
private int _maxRadius = 0;
public DiscManager(long style){
super(style);
DiscField outerDisc = new DiscField(new String[] { "1", "2", "3", "4", "5", "6" },
180, Color.BLUE);
_maxRadius = outerDisc.getRadius();
DiscField middleDisc = new DiscField(new String[] { "1", "2", "3", "4", "5" },
120, Color.GRAY);
middleDisc.setOffset(_maxRadius - middleDisc.getRadius());
DiscField innerDisc = new DiscField(new String[] { "1", "2", "3", "4" },
60, Color.RED);
innerDisc.setOffset(_maxRadius - innerDisc.getRadius());
// order matters here:
add(outerDisc);
add(middleDisc);
add(innerDisc);
}
protected void sublayout(int width, int height) {
setExtent(2 * _maxRadius, 2 * _maxRadius);
// each disc needs to have the same x,y center to be concentric
for (int i = 0; i < getFieldCount(); i++) {
if (getField(i) instanceof DiscField) {
DiscField disc = (DiscField) getField(i);
int xCenter = _maxRadius - disc.getRadius();
int yCenter = _maxRadius - disc.getRadius();
setPositionChild(disc, xCenter, yCenter);
layoutChild(disc, 2 * _maxRadius, 2 * _maxRadius);
}
}
}
protected boolean touchEvent(TouchEvent event) {
int eventCode = event.getEvent();
// Get the touch location, within this Manager
int x = event.getX(1);
int y = event.getY(1);
if ((x >= 0) && (y >= 0) && (x < getWidth()) && (y < getHeight())) {
int field = getFieldAtLocation(x, y);
if (field >= 0) {
DiscField df = null;
for (int i = 0; i < getFieldCount(); i++) {
if (getField(field) instanceof DiscField) {
int r = ((DiscField)getField(field)).getRadius();
// (_maxRadius, _maxRadius) is the center of all discs
if ((x - _maxRadius) * (x - _maxRadius) + (y - _maxRadius) * (y - _maxRadius) <= r * r) {
df = (DiscField)getField(field);
} else {
// touch was not within this disc's radius, so the one slightly bigger
// should be passed this touch event
break;
}
}
}
// Let event propagate to child field
return (df != null) ? df.touchEvent(event) : super.touchEvent(event);
} else {
if (eventCode == TouchEvent.DOWN) {
setFocus();
}
// Consume the event
return true;
}
}
// Event wasn't for us, let superclass handle in default manner
return super.touchEvent(event);
}
}
Finally, a screen to use them:
public class DiscScreen extends MainScreen {
public DiscScreen() {
super(MainScreen.VERTICAL_SCROLL | MainScreen.VERTICAL_SCROLLBAR);
add(new DiscManager(Field.USE_ALL_WIDTH));
}
}
Results

Eclipse: Java, no main method found

I recently took all my code a manually imported it into an eclipse project from BlueJ, I then got use to the settings up "Run Configurations", finally thinking I was home free. Then I ran the code, and I got this error
java.lang.NoSuchMethodError: main
Exception in thread "main"
so I figured I had to add a main method (I never had to do this in BlueJ, why?). So I did that just called the constructor method (in BlueJ I would just create a new object and the JFrame would show). So I did that, same error. After trying different things (such as moving the code in the constructor to a different method etc.). I just put this in for the main method:
public void main(String[] args)
{
System.out.println("Hello, this is main why won't Java find this.");
}
After that I still got the same error, so I then decided to add it to all my classes to make sure it wasn't using another class as the main class. Still same error, so I come to you wondering if any of you have encountered this problem. Also I did search Google and all I found was problems with private classes etc and sense all my classes are public (hey I come from Python :) ). I knew that wasn't the problem. Help Please :)
Picture of my Run Configuration
This is my main method code
WARNING: LONG
import java.awt.event.*;
import java.awt.*;
import javax.swing.*;
public class AppFrame extends JFrame
{
public String status = "Status:";// Status of Applet
public int paint_count = 1;// Number of times applet has been painted
public int[] mousePos = {-1, -1};// Stores Mouse's Last Clicked X and Y Cordinates
public int[] boardPos = {-1, -1};//Stores The Board's X and Y Cordinates
public int[] selectedSquarePos = {-1, -1};
public int[] boardSquare = {-1, -1};//Stores Last Clicked Square
public Color P1_Color = Color.GRAY;
public Color P2_Color = Color.WHITE;
public Color SquareEven = Color.BLACK;
public Color SquareOdd = Color.RED;// pieces move on this one
public int boardHeight = 400;
public int boardWidth = 400;
public boolean pieceSelected = false;
public boolean CheckersPiece = false;
public Board CheckersBoard = new Board();
public Image buffer = null;
public Graphics bg = null;
public void main(String[] args)
{
System.out.println("Hello, this is main why won't Java find this.");
}
public AppFrame()
{
super("JCheckers");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(900,900);
setVisible(true);
buffer = createImage(getWidth(), getHeight());
boardHeight = getHeight() - 40; // 20 pixel border at top and bottom and 20 pixels for blue bar
boardWidth = getWidth() - 40; // 20 pixel border at left and right
bg = buffer.getGraphics();
addMouseListener(new MouseAdapter()
{
public void mouseClicked (MouseEvent e)
{
handleClick(e);
}
}
);
}
public void handleClick(MouseEvent e)
{
/* Handles tracking of mouse clicks; DOES NOT HANDLE DISPLAY, it just updates the data and calls redraw */
mousePos[0] = e.getX();
mousePos[1] = e.getY();
repaint();
}
public void paint(Graphics g)
{
super.paint(g);
render(g);
}
public void render(Graphics g)
{
bg.clearRect(0,0, getWidth(), getHeight());
//Draw Chess Board and Pieces
renderChessBoard(bg, 20, 20);
// Display Info
System.out.println(status);
System.out.println(String.format("Last Mouse Click # (X:%d Y:%d)", mousePos[0], mousePos[1]) );
System.out.println("Paint #" + paint_count );
System.out.println(String.format("Board Square (x:%s, y:%s) %b", boardSquare[0], boardSquare[1], CheckersPiece) );
System.out.println(CheckersBoard.status );
paint_count += 1;
// Draw Image to Screen
g.drawImage(buffer, 0, 25, null);// so it does not get hidden by the blue close/min/max room
}
public boolean isValidSquare(int col, int row)
{
if (col > -1 & col < 8) {return true;}
return false;
}
public void renderChessBoard(Graphics g, int x, int y)
{
/* Renders board and pieces*/
// sense the row squares are well squares then the
// board will be a square also; so we draw it with whatever
// side is smallest, width or height
boardPos[0] = x;
boardPos[1] = y;
drawBoard(g, x, y, boardWidth, boardHeight);
boardSquare = getBoardSquare(mousePos[0], mousePos[1]);
CheckersPiece = isCheckersPiece(boardSquare[0], boardSquare[1]);
boolean validSquare = isValidSquare(boardSquare[0], boardSquare[1]);
if (validSquare)
{
if (CheckersPiece)
{
selectSquare(g, boardSquare[0], boardSquare[1]);
}
else
{
if (pieceSelected)
{
int selectedCol = selectedSquarePos[0];
int selectedRow = selectedSquarePos[1];
int toCol = boardSquare[0];
int toRow = boardSquare[1];
System.out.println(selectedCol + " " + selectedRow + " " + toCol + " " + toRow);
if (!CheckersBoard.move(selectedSquarePos, boardSquare)) // not a valid move;
{
pieceSelected = false;
}
}
}
}
parseBoard(CheckersBoard.board, g);
}
public void drawBoard(Graphics g, int Bx, int By, int Bw, int Bh)
{
int numberRowsDrawed = 0;
int rH = Bh / 8;
int rW = Bw; // Row width is the same as the Board's width because the board and the row share the same sides
while (numberRowsDrawed < 8)
{
int rY = (numberRowsDrawed * rH) + By;
// Row X is the same as the Board X because the board and the row share the same sides
int rX = Bx;
Color EVEN = SquareEven;
Color ODD = SquareOdd;
// Yes Yes The EVEN Color is now odd and vica versa its because rows only now there row counts and so they start at 0 and don't
// include the rows above
if ((numberRowsDrawed % 2) != 0) {EVEN = SquareOdd; ODD = SquareEven;}
drawRow(g, rX, rY, rW, rH, EVEN, ODD);
numberRowsDrawed +=1;
}
}
public void drawRow(Graphics g, int x, int y, int width, int height, Color EVEN, Color ODD)
{
System.out.println("Board Y: " + y);
int squareW = width / 8;
int squareH = height;
int numberSquaresCreated = 0;
while (numberSquaresCreated < 8)
{
// needs a special case because Java's modulo uses division (so it would give a divide by 0 error) STUPID JAVA!!!!!!
if (numberSquaresCreated == 0)
{
g.setColor(EVEN);
g.fillRect(x, y, squareW, squareH);
}
else
{
if (numberSquaresCreated % 2 == 0){g.setColor(EVEN);}
else {g.setColor(ODD);}
int sX = x + (squareW * numberSquaresCreated);
g.fillRect(sX, y, squareW, squareH);
}
numberSquaresCreated +=1;
}
}
public void drawMan(Graphics g, int boardRow, int boardCol, Color pieceColor)
{
int x = boardPos[0];
int y = boardPos[1];
int pixelPosX = x + ((boardWidth / 8) * boardRow);
int pixelPosY = y + ((boardHeight / 8) * boardCol);
g.setColor(pieceColor);
g.fillOval(pixelPosX + 13, pixelPosY + 13, (boardWidth / 8) - 26, (boardHeight / 8) - 26);
}
public void drawKing(Graphics g, int boardRow, int boardCol, Color pieceColor, Color crownColor)
{
drawMan(g, boardRow, boardCol, pieceColor);
g.setColor(crownColor);
int x = boardPos[0];
int y = boardPos[1];
double DsizeFactor = ( (float) boardHeight / 8.0) / 3.75;
int sizeFactor = (int) DsizeFactor;
int pixelPosX = x + ((boardWidth / 8) - sizeFactor) / 2 + ((boardWidth / 8) * boardRow);
int pixelPosY = y + ((boardHeight / 8) - sizeFactor) / 2 + ((boardHeight / 8) * boardCol);
int[] xPoints = {pixelPosX, pixelPosX, pixelPosX + sizeFactor, pixelPosX + sizeFactor, pixelPosX + ((sizeFactor * 3) / 4), pixelPosX + (sizeFactor / 2), pixelPosX + (sizeFactor / 4) };
int[] yPoints = {pixelPosY, pixelPosY + sizeFactor, pixelPosY + sizeFactor, pixelPosY, pixelPosY + (sizeFactor / 2), pixelPosY, pixelPosY + (sizeFactor / 2)};
g.fillPolygon(xPoints, yPoints, 7);
}
public void selectSquare(Graphics g, int bSX, int bSY)
{
g.setColor(Color.YELLOW);
/*+10 is to offset text (the xy cordinates are the bottom left side of the text NOT top left.*/
pieceSelected = true;
int squareX = boardPos[0] + (boardWidth / 8) * bSX;
int squareY = 10 + boardPos[1] + (boardHeight / 8) * bSY;
selectedSquarePos[0] = bSX;
selectedSquarePos[1] = bSY;
g.drawString("Selected", squareX, squareY);
}
// Data Handling and Retreiving methods
public void parseBoard(String[][] Board, Graphics g)
{
int row = 0;
int col = 0;
for (String[] rowOfPieces : Board)
{
for (String piece : rowOfPieces)
{
if (piece != "X")
{
Color PIECE_COLOR = P1_Color;
if (piece.contains("P2")) {PIECE_COLOR = P2_Color;}
if (piece.contains("C"))
{
drawMan(g, col, row, PIECE_COLOR);
}
if (piece.contains("K"))
{
Color Crown_Color = P2_Color;
if (PIECE_COLOR != P1_Color) {Crown_Color = P1_Color;}
drawKing(g, col, row, PIECE_COLOR, Crown_Color);
}
}
col+=1;
}
row +=1;
col = 0;
}
}
public int[] getBoardSquare(int x, int y)
{
//row or col = boardpos - offset / row height or width
if ((x < boardPos[0]) | (y < boardPos[1]) | (x > (boardPos[0] + boardWidth)) | (y > (boardPos[1] + boardHeight)) )
{
int[] BS = {-1, -1};
return BS;
}
int col = (x - boardPos[0]) / (boardWidth / 8);
int row = (y - boardPos[1]) / (boardHeight / 8);
int[] BS = {col, row};
return BS;
}
public boolean isCheckersPiece(int BoardSquareX, int BoardSquareY)
{
int Px = BoardSquareX;
int Py = BoardSquareY;
if (Px == -1 & Py == -1)
{
return false;
}
String Square = CheckersBoard.board[Py][Px];
return Square != "X";
}
}
You forgot static:
public static void main(String[] args)
But in order to really start your application, you should launch it from that method, not merely have it. Here is how to start it:
public static void main(String[] args) {
javax.swing.SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
AppFrame frame = new AppFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
});
}
It's important to note the EXIT_ON_CLOSE - this will make the JVM exit when you press the X button. Otherwise your application will continue running, and you might even not notice.
Make main static.

Categories