Stack Overflow error with ArrayLists - java

I'm trying to write some code to look through an image file for groups of pixels that are the same color.
The way I do this is I iterate through the image (in the form of a 1d integer array with the color's hash code) by pixel to find a pixel of the color that I'm searching for. Once one is found, I do a dfs to find adjacent pixels of the same color and add them to a new object I called a Blob. I use a boolean array to keep track of which pixels have already been added so I don't add identical blobs.
I'm using ArrayList for each Blob object to keep track of the pixel numbers. And then I use another ArrayList of Blobs to store the different groups.
When I try to run this on a simple example, a picture with the top half white and the bottom half bottom, I get a stack overflow error when I try to use a picture that's too large. Specifically, when I try to do this with a 320x240 image, I get the stackoverflow once 2752 pixels are added to the blob.
Am I just not using the right data structure for what I want to do? I read that ArrayLists can store Integer.maxValue objects in them.
My code is pasted below. Any help is greatly appreciated.
//blobfind tests code to find similar pixels of a minimum size and groups them together for analysis later
//purpose is to identify color coded objects through the webcam
//util for ArrayList
import java.util.*;
import java.awt.Color;
import java.io.*;
public class Blobfind2 {
//width and height of image in pixels
private int width;
private int height;
//hash code for the color being searched for
private int colorCode;
//minimum blob size to be added
private int minPixels;
//image in form of array of hashcodes for each pixel
private int[] img;
//keeping track of which pixels have been added to a blob
private boolean[] added;
//keeping track of which pixels have been visited when looking for a new blob
private boolean[] visited;
//debugging variable
private int count;
public Blobfind2(int inwidth, int inheight, int inCCode, int inminPixels, int[] inimage) {
width = inwidth;
height = inheight;
colorCode = inCCode;
minPixels = inminPixels;
img = inimage;
count = 0;
}
//takes hashCodeof color, minimum pixel number, and an image in the form of integer array
public ArrayList findColor() {
//makes an arraylist of "blobs"
ArrayList bloblist = new ArrayList();
//keep track of which pixels have been added to a blob
boolean[] added = new boolean[width * height];
//checks through each pixel
for (int i = 0; i < img.length; i++) {
//if it matches and is not part of a blob, we run dfs to collect all the pixels in that blob
if ((img[i] == colorCode) && (added[i] == false)) {
//visited keeps track of which pixels in the blob have been visited
//refreshed each time a new blob is made
boolean[] visited = new boolean[width*height];
Blob currBlob = new Blob();
dfs(img, currBlob, i, Color.white.hashCode(), added, visited);
//adds the blob to the bloblist if it is of a certain size
if (currBlob.mass() >= minPixels) {
bloblist.add(currBlob);
}
}
}
return bloblist;
}
//recursive algorithm to find other members of a blob
public void dfs (int[] img, Blob blob, int currPixel, int colorCode, boolean[] added, boolean[] visited) {
//System.out.print(currPixel + " - " + count + " ");
count++;
//check current pixel, this only happens on the first pixel
if (visited[currPixel] == false) {
blob.add(img[currPixel]);
added[currPixel] = true;
visited[currPixel] = true;
}
//checks down pixel
if ((currPixel + width < height*width) && (visited[currPixel + width] == false)) {
if (img[currPixel + width] == colorCode) {
blob.add(img[currPixel + width]);
currPixel = currPixel + width;
added[currPixel] = true;
visited[currPixel] = true;
dfs(img, blob, currPixel, colorCode, added, visited);
}
}
//checks up pixel
if ((currPixel - width > 0) && (visited[currPixel - width] == false)) {
if (img[currPixel - width] == colorCode) {
blob.add(img[currPixel - width]);
currPixel = currPixel - width;
added[currPixel] = true;
visited[currPixel] = true;
dfs (img, blob, currPixel, colorCode, added, visited);
}
}
//checks right pixel
if ((currPixel + 1 < width * height) && (visited[currPixel + 1] == false) && (((currPixel + 1) % width) != 0)) {
if (img[currPixel + 1] == colorCode) {
blob.add(img[currPixel + 1]);
currPixel = currPixel + 1;
added[currPixel] = true;
visited[currPixel] = true;
dfs(img, blob, currPixel, colorCode, added, visited);
}
}
//checks left pixel
if ((currPixel - 1 > 0) && (visited[currPixel - 1] == false) && (((currPixel - 1) % width) != width - 1)) {
if (img[currPixel - 1] == colorCode) {
blob.add(img[currPixel - 1]);
currPixel = currPixel - 1;
added[currPixel] = true;
visited[currPixel] = true;
dfs(img, blob, currPixel, colorCode, added, visited);
}
}
return;
}
//test case, makes a new image thats half black and half white
//should only return one blob of size width*height/2
public static void main(String[] args) {
int width = 320;
int height = 240;
//builds the image
int[] img = new int[width * height];
for (int i = 0; i < img.length; i++) {
if (i < img.length/4) {
img[i] = Color.white.hashCode();
} else {
img[i] = Color.black.hashCode();
}
}
//runs blobfind
Blobfind2 bf = new Blobfind2(width, height, Color.white.hashCode(), 1, img);
ArrayList bloblist = bf.findColor();
System.out.println(bloblist.size());
//need to typecast things coming out of arraylists
Blob firstblob = (Blob)bloblist.get(0);
System.out.println(firstblob.mass());
}
private class Blob {
private ArrayList pixels = new ArrayList();
private Blob() {
}
private int mass() {
return pixels.size();
}
private void add(int i) {
pixels.add(i);
}
private ArrayList getBlob() {
return pixels;
}
}
}

The stack overflow error has nothing to do with whether you use an List, or a Map, or any other particular data structure. Those constructs are allocated on the heap. You are seeing your stack overflow error because you make recursive function calls. Each recursive function call allocates memory on the stack. You can increase your -Xss value (e.g java -Xss8m HelloWorld) or you can re-write your algorithm to be non-recursive (assuming your algorithm is correct).

This looks very similar to a flood-fill algorithm. The recursive implementation might blow the stack (e.g. make too many recursive calls) for large blobs, simply because you have to explore 4 neighbours for every pixel. Worst case is an image all in the same blob!
I would try making the stack explicit. You want to avoid the recursion and use a simple loop based approach instead.
public void dfs () {
Stack<Pixel> pixels = new Stack<Pixel>();
pixels.push(currentPixel);
while (!pixels.isEmpty()) {
Pixel x = pixels.pop();
// Do whatever processing on this pixel
Pixel upPixel = getUpPixel();
if (upPixel == colorCode) {
pixels.push(upPixel);
}
// And so on
}
}

Related

Boolean is set true without ever getting to the line setting the boolean true

A normal level (picture 1)
The squares are tiles and the red dots are vertices (picture 2)
What I'm trying to make is a set of blocks (grey things) which can be destroyed (removed) so water can go trough them. As shown in the picture this works most of the time, some blocks have already been removed and water is flowing trough the gaps. The problem is that it doesn't work all the time. Sometimes when a block is removed water doesn't fill the gap and there comes a hole in the water.
The code
public class WaterManager(){
//level is the map, it is the parent containing the tile mesh and the water
public Node level;
Mesh water = new Mesh();
Geometry geo;
Vector3f[] vertices;
//subdivision is used so the mesh is more detailed, a subdivision of 2 means there are 9 vertices per tile (cube) as shown on picture 2
public int subdivision = 2;
//The amount of vertices on the x and z-axis
int xVertices, zVertices;
//The size of one tile, this is 10 in my case
float cubeSize;
//The amount of tiles on the x and z-axis
int xCubes, zCubes;
List<Integer> triangles = new ArrayList<>();
List<Vector3f> emptyCubes = new ArrayList<>();
List<Vector3f> newEmptyCubes = new ArrayList<>();
Dictionary<Vector3f, Tile> tiles = new Hashtable<Vector3f, Tile>();
public void Loop(Vector3f startPos){
newEmptyCubes.clear();
emptyCubes.clear();
boolean loop = true;
List<Vector3f> temp = new ArrayList<>();
//This is the first check to get all the connected blocks that should be filled with water
//including the block clicked on
GetConnectedEmptyTiles(startPos, newEmptyCubes);
//If there is more then 1 block which should be filled than loop through all the blocks
//and get the ones connected to those that should be filled with water
if (newEmptyCubes.size() > 1){
do{
for(Vector3f cube : newEmptyCubes){
GetConnectedEmptyTiles(cube, temp);
}
emptyCubes.addAll(newEmptyCubes);
newEmptyCubes.clear();
if (temp.size() <= 0)
loop = false;
newEmptyCubes.addAll(temp);
temp.clear();
}while(loop);
} else emptyCubes.addAll(newEmptyCubes);
//This creates the mesh
PaintEmptyTiles();
}
private void GetConnectedEmptyTiles(Vector3f pos, List<Vector3f> list){
//The blocks connected to a given posistion including the given position
Vector3f[] connectedTilesPlusPos = new Vector3f[]{
new Vector3f(pos.x + cubeSize, pos.y, pos.z),
new Vector3f(pos.x - cubeSize, pos.y, pos.z),
new Vector3f(pos.x, pos.y, pos.z + cubeSize),
new Vector3f(pos.x, pos.y, pos.z - cubeSize),
pos
};
//If the removed block is not connected to water then it shouldn't be filled
//with water
boolean connectedToWater = false;
List<Vector3f> tempList = new ArrayList<>();
//Check if the given position exists
if (tiles.get(pos) != null){
System.out.println("tile has water: " + tiles.get(pos).water);
//Loop through all the connected blocks including the given position
//and if they should be filled with water than add them to tempList
for(Vector3f tile : connectedTilesPlusPos){
if (tiles.get(tile) != null){
//Check if there is a block at the position (tile), if there
// is not than it can be filled with water
if (tiles.get(tile).tileObj == null){
//Check if the position already has water in it **(this is were it goes wrong)**
if (tiles.get(tile).water == false){
tempList.add(tile);
tiles.get(tile).water = true;
} else connectedToWater = true; //If there is already water attached to the
//the given position then it is connected to water
}
}
}
if (!connectedToWater)
tempList.clear();
else list.addAll(tempList);
}
}
private void PaintEmptyTiles(){
for(Vector3f cube : emptyCubes){
MakeTriangles(cube);
}
MakeMesh();
}
public void MakeTriangles(Vector3f pos){
//Gets an array of vertices that are used to create water at a given position as shown in picture 2
Vector3f[] verticesToUse = GetVerticesForPosition(pos);
List<Integer> indexes = new ArrayList<>();
for(int i = 0; i < vertices.length; i++){
for(Vector3f vert : verticesToUse){
if (vertices[i].equals(vert)){
indexes.add(i);
}
}
}
int xVerticesSize = rowSize(indexes, tiles.get(pos));
int zVerticesSize = (int)indexes.size() / xVerticesSize;
int verticesLenght = (int)Math.round(Math.sqrt(vertices.length));//The amount of vertices in one row.
int i = 0;
for(int x = 0; x < xVerticesSize; x++){
for (int z = 0; z < zVerticesSize; z++){
int vert = indexes.get(i);
if (indexes.contains(vert + verticesLenght + 1)){
//Creates a quad, since the water is build from small quads
triangles.add(vert);
triangles.add(vert + 1);
triangles.add(vert + verticesLenght + 1);
triangles.add(vert + verticesLenght + 1);
triangles.add(vert + verticesLenght);
triangles.add(vert);
}
i++;
}
}
}
private Vector3f[] GetVerticesForPosition(Vector3f pos){
List<Vector3f> tempVertices = new ArrayList<>();
for (Vector3f vertex : vertices) {
if (vertex != null){
float distance = DistanceOnXAndZ(pos, vertex);
float roundedDistance = roundedTo(distance, cubeSize / subdivision);
if (distance < cubeSize || (roundedDistance <= cubeSize && vertex.y <= -100 )){
tempVertices.add(vertex);
}
}
}
Vector3f[] returnVertices = new Vector3f[tempVertices.size()];
returnVertices = tempVertices.toArray(returnVertices);
return returnVertices;
}
//This calculates the distance from a point to another point but only using the x and z-axis
private float DistanceOnXAndZ(Vector3f origin, Vector3f target){
float originX = origin.x;
float originZ = origin.z;
float targetX = target.x;
float targetZ = target.z;
float xDistance = targetX - originX;
float zDistance = targetZ - originZ;
float distance = (float)Math.sqrt((xDistance * xDistance) + (zDistance * zDistance));
return distance;
}
private void MakeMesh(){
water.setBuffer(Type.Position, 3, BufferUtils.createFloatBuffer(vertices));
int[] tri = new int[triangles.size()];
for(int i = 0; i < triangles.size(); i++){tri[i] = triangles.get(i);}
water.setBuffer(Type.Index, 3, BufferUtils.createIntBuffer(tri));
water.updateBound();
geo = new Geometry("Water", water);
Material mat = new Material(assetManager,
"Common/MatDefs/Misc/Unshaded.j3md");
mat.setColor("Color", ColorRGBA.Blue);
geo.setMaterial(mat);
level.attachChild(geo);
}
}
//The tile Object
public class Tile {
//not used anymore, but not deleted yet
public enum CellType {
LEFTORRIGHTSIDE, OTHER
}
public CellType cellType;
//The cube the Tile contains, if there is no cube then this is null
public Spatial tileObj;
//True if the tile contains water, false if not
public boolean water = false;
public Tile(CellType type, Spatial obj){
//Nothing is done with cellType
cellType = type;
tileObj = obj;
}
}
The reason
The reason this occurs is still unknown, but I think it has something to do with the water boolean in Tile already set to true even when it has a cube (grey thing) in it.
Things to know
tiles is a list containing all the positions that contain a tile, if it contains a block then tileObj is not null. If the position contains water then the water boolean should be true.
PaintEmptyTiles(), in Loop() is a function that creates the water mesh
When a block is destroyed by clicking on it, the Loop() function is called and the tileObj in Tile is set to null in the tiles list
A tile is a square with, containing either a grey block, water or nothing.

Identifying specific Blobs in Processing

I am working in Processing making contour maps from height maps using the blobDetection library. My end goal is to laser cut the blobs to make sort of architectural landscape maquettes.
Currently I can obtain the contours and export the contour map as SVG which is great, but I want to be able to identify each blob (or contour, or ring) and to then be able to manipulate each contour separately. Namely I want to reposition them on the window so that they are not on top of each other and do not overlap. I also want to assign coordinates to each individual blob so they would be easy to know where each blob goes once it has been laser cut.
Here is the code (from an example provided by author v3ga):
import processing.svg.*;
import blobDetection.*;
import peasy.*;
import processing.pdf.*;
PeasyCam cam;
PImage img;
float levels = 10;
float factor = 10;
float elevation = 125;
float colorStart = 0;
float colorRange = 160;
BlobDetection[] contours = new BlobDetection[int(levels)];
boolean recording = false;
void keyPressed(){
if (key == 'r' || key == 'R'){
recording = !recording;
}
}
void setup() {
size(1000,800,P3D);
surface.setResizable(true);
img = loadImage("map1.gif");
surface.setSize(img.width, img.height);
cam = new PeasyCam(this, img.width, img.height, 0, 500);
colorMode(HSB, 360, 100, 100);
for (int i=0; i<levels; i++){
contours[i] = new BlobDetection(img.width, img.height);
contours[i].setThreshold(i/levels);
contours[i].computeBlobs(img.pixels);
}
}
void draw(){
if (recording){
beginRecord(SVG, "frame_####.svg");
}
for (int i=0; i<levels; i++){
drawContours(i);
}
if (recording) {
endRecord();
recording = false;
}
}
void drawContours(int i) {
Blob b;
EdgeVertex eA,eB;
for (int n=0 ; n<contours[i].getBlobNb() ; n++) {
b=contours[i].getBlob(n);
if (b!=null) {
stroke(250,75,90);
for (int m=0;m<b.getEdgeNb();m++) {
eA = b.getEdgeVertexA(m);
eB = b.getEdgeVertexB(m);
if (eA !=null && eB !=null)
line(
eA.x*img.width, eA.y*img.height,
eB.x*img.width, eB.y*img.height
);
}
}
}
}
After testing a few things I think the best is to create and array of objects that contain information for each blob (x and y coordinates, level) and to fill this array in the drawContours method. However I am having a lot of trouble obtaining the right information to store in this array.
So my questions are:
How to identify x,y coordinates of complex shapes such as these blobs
How to reposition the blobs once I have their info store in an array
Any suggestion, even using other techniques (i.e. not Processing) would be greatly appreciated.
To display the blobs in 3D add the height_scale constant at the top and modify the drawContours function to the following:
final int HEIGHT_SCALE = 10; // amount of z space between each blob
void drawContours(int i) {
Blob b;
EdgeVertex eA,eB;
for (int n=0 ; n<contours[i].getBlobNb() ; n++) {
b=contours[i].getBlob(n);
if (b!=null) {
stroke(250,75,90);
beginShape();
for (int m=0;m<b.getEdgeNb();m++) {
eA = b.getEdgeVertexA(m);
eB = b.getEdgeVertexB(m);
if (eA !=null && eB !=null)
vertex(eA.x*img.width, eA.y*img.height, i*HEIGHT_SCALE);
vertex(eB.x*img.width, eB.y*img.height, i*HEIGHT_SCALE);
}
endShape(CLOSE);
}
}
}
Keep in mind I have not run this code, and I haven't used the framework that you are using however the beginShape(), vertex(), and endShape() functions should allow you to create the same lines between edges, and I added a z coordinate to the vertex's so that they are separated in height.
Just realized you could also just go with the following because line(x1,y1,x2,y2) can also take line(x1,y1,z1,x2,y2,z2):
final int HEIGHT_SCALE = 10; // amount of z space between each blob
void drawContours(int i) {
Blob b;
EdgeVertex eA,eB;
for (int n=0 ; n<contours[i].getBlobNb() ; n++) {
b=contours[i].getBlob(n);
if (b!=null) {
stroke(250,75,90);
for (int m=0;m<b.getEdgeNb();m++) {
eA = b.getEdgeVertexA(m);
eB = b.getEdgeVertexB(m);
if (eA !=null && eB !=null)
line(eA.x*img.width, eA.y*img.height, i*HEIGHT_SCALE, eB.x*img.width, eB.y*img.height, i*HEIGHT_SCALE);
}
}
}
}

Slow map in java

I'm making a game in java, is a rpg, however, only with the map the game is slow.
The map is made ​​in TiledMap Editor, therefore, an XML that is read and loaded into an ArrayList. My PC is a dual-core 3.0, 4GB RAM, 1GB Video.
The do the rendering is done as follows:
//method of tileset class
public void loadTileset(){
positions = new int[1 + tilesX * tilesY][2];
int yy = 0;
int xx = 0;
int index = 0;
// save the initial x and y point of each tile in an array named positions
// positions[tileNumber] [0] - X position
// positions[tileNumber] [1] - Y position
for(int i = 1 ; i < positions.length; i++){
if(index == tilesX ){
yy += tileHeight;
xx = 0;
index = 0;
}
positions[i][0] = xx;
positions[i][1] = yy;
xx += tileWidth;
index++;
}
}
//method of map class
public void draw(Graphics2D screen){
//x and y position of each tile on the screen
int x = 0; int y = 0;
for(int j = 0; j < 20 ; j++){
for(int i = initialTile ; i < initialTile + quantTiles ; i++){
int tile = map[j][i];
if(tile != 0){
screen.drawImage(tileSet.getTileImage().getSubimage(tileSet.getTileX(tile), tileSet.getTileY(tile),tileSet.getTileWidth(), tileSet.getTileHeight()),x,y,null);
}
x += tileSet.getTileWidth();
}
x = 0;
y += tileSet.getTileHeight();
}
}
Am I doing something wrong?
Note: I'm new to the forum and to make matters worse I do not understand very much English, so excuse any mistake.
First of all, you should not create the subimages for the tiles during each call. Strictly speaking, you should not call getSubimage at all for images that you want to paint: It will make the image "unmanaged", and this can degrade rendering performance by an order of magnitude. You should only call getSubimage for images that you do not want to render - for example, when you are initially creating individual images for the tiles.
You obviously already have a TileSet class. You could add a bit of functionality to this class so that you can directly access images for the tiles.
Your current code looks like this:
screen.drawImage(
tileSet.getTileImage().getSubimage(
tileSet.getTileX(tile),
tileSet.getTileY(tile),
tileSet.getTileWidth(),
tileSet.getTileHeight()),
x,y,null);
You could change it to look like this:
screen.drawImage(tileSet.getTileImage(tile), x,y,null);
The getTileImage(int tile) method suggested here could then obtain tiles that have been stored internally.
I'll sketch a few lines of code from the tip of my head, you'll probably be able to transfer this into your TileSet class:
class TileSet
{
private Map<Integer, BufferedImage> tileImages;
TileSet()
{
....
prepareTileImages();
}
private void prepareTileImages()
{
tileImages = new HashMap<Integer, BufferedImage>();
for (int tile : allPossibleTileValuesThatMayBeInTheMap)
{
// These are the tiles that you originally rendered
// in your "draw"-Method
BufferedImage image =
getTileImage().getSubimage(
getTileX(tile),
getTileY(tile),
getTileWidth(),
getTileHeight());
// Create a new, managed copy of the image,
// and store it in the map
BufferedImage managedImage = convertToARGB(image);
tileImages.put(tile, managedImage);
}
}
private static BufferedImage convertToARGB(BufferedImage image)
{
BufferedImage newImage = new BufferedImage(
image.getWidth(), image.getHeight(),
BufferedImage.TYPE_INT_ARGB);
Graphics2D g = newImage.createGraphics();
g.drawImage(image, 0, 0, null);
g.dispose();
return newImage;
}
// This is the new method: For a given "tile" value
// that you found at map[x][y], this returns the
// appropriate tile:
public BufferedImage getTileImage(int tile)
{
return tileImages.get(tile);
}
}

Compare color shades?

I have two colors, how do I check if they are the same color but just a different shade? I've been trying but I cant seem to figure it out, I really don't know what I'm doing lol... This is what I have so far:
import java.awt.Color;
public class Sandbox {
public Sandbox() {
Color c = new Color(5349322);
int r, g, b;
r = c.getBlue();
g = c.getGreen();
b = c.getRed();
System.out.println("Red: " + r);
System.out.println("Green: " + g);
System.out.println("Blue: " + b);
}
private boolean FindColorTol(int intTargetColor, int Tolerance) {
Color targetColor = new Color(intTargetColor);
Color imgColor = new Color(5349322);
int targetRED = targetColor.getBlue(),
targetGREEN = targetColor.getGreen(),
targetBLUE = targetColor.getRed(),
imgRED = imgColor.getBlue(),
imgGREEN = imgColor.getGreen(),
imgBLUE = imgColor.getRed();
return false;
}
private int getLargest(int...values) {
int largest = 0;
for(int i = 0; i < values.length; i++) {
if(values.length > i + 1) {
if(values[i] > values[i + 1])
largest = values[i];
else
largest = values[i + 1];
}
}
return largest;
}
public static void main(String[] args) {
new Sandbox();
}
}
And also, why does Color.getRed(), return the value for blue and, Color.getBlue() returns the value for returns the value for red? I am using this to find RGB values: http://www.colorschemer.com/online.html
I am trying to use this to find a specified color within an image.
In colour theory, a shade is what you get by mixing a colour with different amounts of black. So you can easily check if two RGB triplets correspond to different shades of the same colour by normalizing their values
max1 = max(r1,g1,b1);
max2 = max(r2,g2,b2);
if ( approxEQ(r1/max1,r2/max2,DELTA) &&
approxEQ(g1/max1,g2/max2,DELTA) &&
approxEQ(b1/max1,b2/max2,DELTA) ) {
/* Same colour, different shades */
}
(where, obviously, max(a,b,c) returns the largest of three parameters, and approxEQ(a,b,d) returns true if |a-b|≤d, or false otherwise.)
If you want to check for tints as well, you would be better off converting your RGB values to HSV or HSL.
Maybe HSL Color will help. Strictly speaking I think the Hue and Saturation would need to be the same values.

How to set positions of snakes and ladders in java Snake and Ladder game?

I am developing a snake and ladder game in java, that is configurable-that is, you can set the 'head' and 'tail' positions for snakes and ladders in it.I have tried loading snake images and re-sizing them appropriately for fitting in the bounds of the head and tail positions, but that seems to affect the quality of the images.
Can anyone suggest a better method/algorithm?
Help greatly appreciated.
I would suggest splitting the snake graphics into sections - a head, a tail and one or more middle body sections. Then, depending on the required length of snake you can construct it from drawing the middle section as long as required.
The approach you are using has two "problems" which may cause poor quality:
You are (I assume) upscaling graphic images, which will cause blockiness.
If you change the scale of both x and y axis (eg zoom in) long snakes will be fatter and wider than short snakes, which isn't what people expect to see.
I would modify fd's solution somewhat. Proceed as follows:
Prepare a graphic for the head, the tail, and a single section of middle so you can chain any number of middle sections together.
When you need to draw a snake, calculate its length. Then see how many middle sections you need for the whole snake to be equal to or greater than this calculated length.
Create a bitmap buffer of the correct size to hold a horizontal (or vertical!) snake with the correct number of middle sections. Draw the background as transparent, then draw the snake into this bitmap. It will typically be slightly longer than you need.
Scale and rotate the bitmap and place it at the correct location on your board.
You will still be scaling to some extent, but the scale factor should be small enough to not be obvious. For example, if you need 5.5 middle sections to make an exact fit, then you would draw 6 middle sections and then scale the whole snake by about 5.5/6 to make it exactly the right length. This is less than 10% change, so shouldn't be obvious.
This solves the problems above:
The width of the snake will only vary slightly
You only downscale, never upscale.
It has the following practical benefits:
You only have to lay out the snake horizontally (or vertically), which is easy to do
It should be very efficient. All the real work is done by the graphics library which has to rotate, zoom and place the constructed snake. On most platforms these operations are implemented as a transformation matrix which is calculated in hardware and hence very fast. I use this approach extensively in an Android app I wrote, and it is extremely fast.
Note that the more "wiggles" in the snake, the less your scaling factor will differ from 1.0 and hence the less variation in width.
public class SankesAndLadder {
static int turn = 0;
static int[] gameBoard = new int[100];
static Map<Integer, Integer> ladderPositionValue;
static Map<Integer, Integer> snakePositionValue;
static int userPosition = 0;
static int computerPosition = 0;
public static void populateBoard() {
ladderPositionValue = new HashMap<>();
snakePositionValue = new HashMap<>();
// SUPPOSE WE HAVE 10 LADDERS AND 10 SNAKES
for (int i = 0; i < 10; i++) {
int ladderPositionKey = (int) (Math.random() * 85) + 10;
if (!ladderPositionValue.keySet().contains(ladderPositionKey)) {
ladderPositionValue.put((int) (Math.random() * 100) + 1, (int) (Math.random() * 10) + 5);
} else {
i--;
}
int snakePositionKey = (int) (Math.random() * 95) + 15;
if (!ladderPositionValue.keySet().contains(ladderPositionKey) &&
!snakePositionValue.keySet().contains(ladderPositionKey)) {
snakePositionValue.put((int) (Math.random() * 100) + 1, (int) (Math.random() * 10) + 5);
} else {
i--;
}
}
}
public static int rollDice(int repeat) {
System.out.println("ROLLING DICE...PRESS ANY KEY TO CONTINUE:");
Scanner in = new Scanner(System.in);
String cont = in.nextLine();
int rolledNo = (int) (Math.random() * 6) + 1;
if (rolledNo == 6) {
System.out.println("NUMBER IS:" + rolledNo + ". ANOTHER CHANCE.");
rollDice(repeat++);
} else {
System.out.println("NUMBER IS:" + rolledNo);
}
int finalCount = rolledNo + (repeat * 6);
return finalCount;
}
public static boolean userTurn() {
if (turn % 2 == 0) {
turn++;
return true;
}
turn++;
return false;
}
public static void newUserPosition(int rolledNo) {
userPosition = userPosition + rolledNo;
System.out.println("YOUR NEW POSITION IS:" + userPosition);
userPosition = checkSnakeOrLadder(userPosition);
}
public static void newComputerPosition(int rolledNo) {
computerPosition = computerPosition + rolledNo;
computerPosition = checkSnakeOrLadder(userPosition);
}
private static int checkSnakeOrLadder(int position) {
if (snakePositionValue.keySet().contains(position)) {
System.out.println("AAAAAAAAAAAHHHH ... BITTEN BY SNAKE");
position = position - snakePositionValue.get(position);
} else if (ladderPositionValue.keySet().contains(position)) {
System.out.println("YAAAAAAAY.. GOT A LADDER");
position = position + ladderPositionValue.get(position);
}
return position;
}
public static void main(String args[]) {
System.out.println("WELCOME TO SNAKES & LADDER");
System.out.println("***************************");
populateBoard();
while (userPosition != 100 && computerPosition != 100) {
if (userTurn()) {
System.out.println("YOUR TURN:");
int rolledNo = rollDice(0);
System.out.println("MOVING YOUR POSITION:");
newUserPosition(rolledNo);
} else {
System.out.println("COMPUTER TURN:");
int rolledNo = rollDice(0);
System.out.println("MOVING COMPUTER POSITION:");
newComputerPosition(rolledNo);
}
}
if (userPosition == 100) {
System.out.println("YOUR ARE THE WINNER!!!!");
} else if (computerPosition == 100) {
System.out.println("COMPUTER WON!!!!");
}
}
}

Categories