So i'm currently having an issue whereby i need to populate a cuboid with certain blocks , now basically its a mine containing different ores in minecraft , but it needs to be randomly based off of some compositions for example :
public class Composition {
private ItemStack stack;
private float percentage;
public Composition(ItemStack stack, float percentage) {
this.stack = stack;
this.percentage = percentage;
}
public ItemStack getStack() {
return stack;
}
public float getPercentage() {
return percentage;
}
public void setStack(ItemStack stack) {
this.stack = stack;
}
public void setPercentage(float percentage) {
this.percentage = percentage;
}
}
then the code that resets this mine is as follows :
for ( Block b : region ) {
float chance = r.nextFloat();
int select = random.nextInt(compositions.size());
Composition comp = compositions.get(select);
if (chance <= comp.getPercentage()) {
b.setType(comp.getStack().getType());
b.setData((byte) comp.getStack().getDurability());
} else {
b.setType(defaultType.getType());
b.setData((byte) defaultType.getDurability());
}
Basically the region is a List of blocks , each block has a type.
as u can see what i tried to do was to generate a random chance float /percentage that the single block , can/should be changed
then i randomly pull an element out of this list of Compositions
based on the percentage of the random chance and the composition percentage i myt set the block to the type of the composition , however i feel like this is not accurate at all.
if someone could come up with a better way or idea to do this that would me a lot thanks.
I'm not sure if my explanation makes sense so bottom line is i'm trying to form a mine/cuboid of random blocks of different types based on compositions.
EG :
Mine has 100 x blocks
Compositions :
IRON_ORE 20 % (0.2F) = 20x Blocks should be IRON
STONE 80 % (0.8F) = 80x Blocks should be STONE
so the whole mine should contain 20% IRON_ORE randomly though not like placed 1 after the other.
STONE will basically take up the whole percentage of the mine but there should be bits of iron randomly seen.
Edited.
I think i managed to find a sort of solution , but if anyone has other opinions or ideas please let me know.
Random r = new Random();
float ratio = region.getBlocks().size() / 100;
List<Block> blocks = new ArrayList<>(region.getBlocks());
for (Composition c : compositions) {
double blocksToChange = Math.floor((float)(ratio * c.getPercentage()));
for(int i = 0; i <= blocksToChange ; i++){
if( blocks.size() <= 0 ) break;
Block b = blocks.get(r.nextInt(blocks.size()));
//Change block
b.setType(c.getStack().getType());
b.setData((byte) c.getStack().getDurability());
//Remove it
blocks.remove(b);
}
}
/// This is for the remaining blocks that cant be calculated with the Math.floor.
for(Block b : blocks){
Composition c = compositions.get(r.nextInt(compositions.size()));
b.setType(c.getStack().getType());
b.setData((byte) c.getStack().getDurability());
}
Related
Let's say we have a 2D-boolean Array as a presentation of a maze, the size of the Array is not fixed and random. The walls are depicted as true:
boolean[][] mazeArray = new boolean[width][height];
The exit of the maze is at a fixed Index. How can I check wether the traversion has reached this certain index or not?
My idea was to create an int[ ] to keep track of the position, it gets updated and overwritten with every step:
int[] location = {1,0};
... But I don't understand why my check in the while-loop doesn't work:
while( location[0] != (maze[0].length-1) && location[1] != (maze[1].length-2) ) {
// traversion with pledge algorithm
}
You're making everything so much harder for yourself. Go easier ways.
Use simpler locations handling
Instead of a location[], simlpy use int destinyX and int destinyY. And as your current position, you should use int positionX and int positionY.
If you'd like the OO-stlye more, or maybe wanna keep the gates open for a solution in 3D or n-D, you could introduce a Location class that has X and Y, and all movement and checks could be handled by that class. Then you'd have Location targetLocation = new Location(x,y); and your current position as Location currentPosition = new Location(x,y);. You then could check with if (currentPosition.equals(targetLocation))...; or in your case while(!currentPosition.equals(targetLocation)) {...}
It seems you have misunderstood the array.length function, or you're using it in an awfully inconvenient way.
At the moment you're blindly shooting at the array lengths of maze[]. This is bad for 2 reasons:
array lengths should not have anything to do with positions inside the array (logical paradox), and
because you could never freely move your destination, it would always stick to the right or bottom outsides of the maze
Use the positioning above, this will clear up that problem.
Suggestion: use a byte[][] or enum[][] for maze
At the moment, you only know if you have a wall at a certain location. If you wanna include other elements, like water, treasure, or the target location, wormholes etc, then you should either:
Use a byte[][] and fill it with values
value 0 could be pathways
value 1 could be walls
value 2 could be the exit
value 3 could be the water etc.
Use constants, like static public final int WATER_CODE = 3;
Or, alternatively, create your own enum:
public enum LocationType {PATH, WALL,EXIT,WATER}
and then have maze be like:
LocationType[][] mazeArray = new LocationType[width][height];
and everything is PATH in the beginning, and you can set up WALLS like this:
mazeArray[x][y] = LocationType.WALL;
or water:
mazeArray[x][y] = LocationType.WATER;
Use class or interface for maze[][]
For the sake of Wormhole or extended functionality, you could also use a class instead of an enum:
abstract class LocationType {}
and then implement certain types, like
class Wall extends LocationType {}
or even
class Wormhole extends LocationType {
public Location leadsTo() { /* */ };
}
and
class Treasure extends LocationType {
public int getAmoundOfGoldCoinsFound() { /* */ };
}
If you implement LocationType as an interface, replace 'extends' by 'implements'
The problem with your code is that you check wrong items in your maze array:
maze[0] is the first "line" of your 2d-array
maze[1] is the second "line" of your 2d-array
Proper way of traversing 2d-array is following (I've replaced your location array with separate x and y variables to better visualize the algorithm).
My algorithm enters the 2d mazeArray line by line and then iterates each line's elements.
public class Maze {
public static void main(String[] args) {
int width = 20;
int height = 20;
boolean[][] mazeArray = new boolean[width][height];
int x = 0;
int y = 0;
while (y < mazeArray.length) {
while (x < mazeArray[y].length) {
System.out.println("Traverse at (" + x + ", " + y + ")");
x += 1;
}
x = 0;
y += 1;
}
}
}
Before I start I want to state that I am learning, I am completely new. Here i have added as much detail as possible.
So, I have an array of textures of different colors, and set a Random to randomize the textures, Like this:
Texture[] bubbles;
Random random = new Random();
int lowRange = 0;
int highRange = 3;
int result = random.nextInt(highRange -lowRange) + 1; //my random
ArrayList<Integer> BubbleXs = new ArrayList<>();
ArrayList<Integer> BubbleYs = new ArrayList<>();
#Override
public void create () {
bubbles = new Texture[4];
bubbles[0] = new Texture("blue.png");
bubbles[1] = new Texture("red.png");
bubbles[2] = new Texture("green.png");
bubbles[3] = new Texture("yellow.png");
}
Then I proceed to draw the texture at a random color falling from the top of the screen using a for loop, like this:
#Override
public void render () {
if (BubbleCount < 120) {
BubbleCount++;
} else {
BubbleCount = 0;
makeBubble();
}
public void makeBubble () {
float width = random.nextFloat() * Gdx.graphics.getWidth();
BubbleXs.add((int)width);
BubbleYs.add(Gdx.graphics.getHeight());
}
for (int i=0; i < BubbleYs.size(); i++) {
batch.draw(bubbles[result], BubbleXs.get(i), BubbleYs.get(i));
BubbleYs.set(i, BubbleYs.get(i) -4);
}
and it draws the textures at a a random perfectly, but only once, when its created, I want them to be a new random each time its looped, so its a different one every time one falls. why is it not doing that? what am I missing?
Thanks in advance!
Edit: I checked out this post: Change texture with random
but its not really helping any.
The reason they're all the same is that you assign a value to result only once, when it is initialized, and you never change it. If you want it to change, you need to use your Random to assign another value.
Your formula random.nextInt(highRange -lowRange) + 1 isn't right if you're wanting a random number between lowRange and highRange inclusive. That's giving you a number between lowRange + 1 and highRange + 1. The correct formula would be random.nextInt(highRange - lowRange) + lowRange.
The way to solve this would be create an additional variable for each bubble that stores the random int. You are already keeping a list for each of the two variables you're already storing (x and y). You could theoretically add a third, but it's to the point where you really just need to create a class for each bubble, so you can expand it easily if you add more features.
class Bubble {
int x;
int y;
int textureIndex;
}
Then replace your two array lists with:
ArrayList<Bubble> bubbles = new ArrayList<>();
And your makeBubble method should look like this:
void makeBubble(){
Bubble bubble = new Bubble();
bubble.x = (int)(random.nextFloat() * Gdx.graphics.getWidth());
bubble.y = Gdx.graphics.getHeight();
bubble.textureIndex = random.nextInt(3);
}
And your for loop for drawing them would look like this. I changed your textures array name here to bubbleTextures so bubbles can be used for the list of Bubble objects.
for (Bubble bubble : bubbles) {
batch.draw(bubbleTextures[bubble.textureIndex], bubble.x, bubble.y);
bubble.y -= 4;
}
There are numerous other issues I'm seeing with your code, but I think it would be overwhelming to explain them all. This at least gets you going in the right direction.
I have a list of islands/areas, initially white. All vertices connected to each other by 1 edge must have opposite colours. (Either black or white). I wish to use the minimum amount of black to colour the islands. Each island has a different size, and index of islands start from 1.
I have used DFS, and a global object to track both the white and black areas, then giving me the minimum of the two. Assuming the entire graph is not connected and has multiple components. I have reset the global variable for every new component. I don't get the correct answers, and I have no idea where is the mistake in my logic. (I am pretty sure my DFSrec method is correct though)
// this will be the DFS spanning tree for 1 component
private void DFSrec(int v,
boolean[] visitedArr, int[] predecessorArr) {
visitedArr[v] = true;
if (islandsList.get(v).colour.equals("WHITE")) {
p.whiteArea += islandsList.get(v).area;
} else {
p.blackArea += islandsList.get(v).area;
}
for (int neighbour : adjList.get(v)) {
if (!visitedArr[neighbour]) {
predecessorArr[neighbour] = v;
if (islandsList.get(v).colour.equals("WHITE")) {
islandsList.get(neighbour).setColour("BLACK");
} else {
islandsList.get(neighbour).setColour("WHITE");
}
DFSrec(neighbour, visitedArr, predecessorArr);
}
}
}
// covers all components
private int DFS(boolean[] visitedArr, int[] predecessorArr, int numIslands){
int minArea = 0;
for (int v = 1; v <= numIslands; v++) {
if ( !visitedArr[v] ) {
DFSrec(v, visitedArr, predecessorArr);
int minAreaOfComponent = p.minArea();
minArea += minAreaOfComponent;
p = new Pair();
}
}
return minArea;
}
Okay, the DFS answer works.
I just realized that the problem occurred because I didn't add in ALL the edges, because the graph is undirected. If I add edge 2 -> 3 in the list, I needed to add in 3 -> 2 as well.
I have an ArrayList of colors and their frequency of appearance. My program should calculate a reordering of those items that maximizes the minimum distance between two equal bricks.
For example, given input consisting of 4*brick 1 (x), 3*brick 2 (y), and 5*brick 3 (z), one correct output would be: z y x z x z y x z x y.
My code does not produce good solutions. In particular, sometimes there are 2 equal bricks at the end, which is the worst case.
import java.util.ArrayList;
import java.util.Collections;
public class Calc {
// private ArrayList<Wimpel> w = new ArrayList<Brick>();
private String bKette = "";
public String bestOrder(ArrayList<Brick> w) {
while (!w.isEmpty()) {
if (w.get(0).getFrequency() > 0) {
bChain += w.get(0).getColor() + "|";
Brick brick = new Wimpel(w.get(0).getVariant(), w.get(0).getFrequency() - 1);
w.remove(0);
w.add(brick);
// bestOrder(w);
} else {
w.remove(0);
}
bestOrder(w);
}
return bOrder;
}
public int Solutions(ArrayList<Wimpel> w) {
ArrayList<Brick> tmp = new ArrayList<Brick>(w);
int l = 1;
int counter = (int) w.stream().filter(c -> Collections.max(tmp).getFrequency() == c.getFrequency()).count();
l = (int) (fakultaet(counter) * fakultaet((tmp.size() - counter)));
return l;
}
public static long fakultaet(int n) {
return n == 0 ? 1 : n * fakultaet(n - 1);
}
}
How can make my code choose an optimal order?
We will not perform your exercise for you, but we will give you some advice.
Consider your current approach: it operates by filling the result string by cycling through the bricks, choosing one item from each brick in turn as long as any items remain in that brick. But this approach is certain to fail when one brick contains at least two items more than any other, because then only that brick remains at the end, and all its remaining items have to be inserted one after the other.
That is, the problem is not that your code is buggy per se, but rather that your whole strategy is incorrect for the problem. You need something different.
Now, consider the problem itself. Which items will appear at the shortest distance apart in a correct ordering? Those having the highest frequency, of course. And you can compute that minimum distance based on the frequency and total number of items.
Suppose you arrange these most-constrained items first, at the known best distance.
What's left to do at this point? Well, you potentially have some more bricks with lesser frequency, and some more slots in which to accommodate their items. If you ignore the occupied slots altogether, you can treat this as a smaller version of the same problem you had before.
Currently I have this for code and my game either uses way to much memory when generating (over a GB) or if I set it low, it will give a
WORLD_SIZE_X & WORLD_SIZE_Z = 256;
WORLD_SIZE_Y = 128;
Does anyone know how I could improve this so it doesn't use so much RAM?
Thanks! :)
public void generate() {
for(int xP = 0; xP < WORLD_SIZE_X; xP++) {
for(int zP = 0; zP < WORLD_SIZE_Z; zP++) {
for(int yP = 0; yP < WORLD_SIZE_Y; yP++) {
try {
blocks[xP][yP][zP] = new BlockAir();
if(yP == 4) {
blocks[xP][yP][zP] = new BlockGrass();
}
if(yP < 4) {
blocks[xP][yP][zP] = new BlockDirt();
}
if(yP == 0) {
blocks[xP][yP][zP] = new BlockUnbreakable();
}
} catch(Exception e) {}
}
//Tree Generation :D
Random rX = new Random();
Random rZ = new Random();
if(rX.nextInt(WORLD_SIZE_X) < WORLD_SIZE_X / 6 && rZ.nextInt(WORLD_SIZE_Z) < WORLD_SIZE_Z / 6) {
for(int j = 0; j < 5; j++) {
blocks[xP][5 + j][zP] = new BlockLog();
}
}
}
}
generated = true;
}
Delay object creation until you really need to access one of these voxels. You can write a method (I'm assuming Block as the common subclass of all the Block classes):
Block getBlockAt( int x, int y, int z )
using code similar what you have in your threefold loop, plus using a hash map Map<Integer,Block> for storing the random stuff, e.g. trees: from x, y and z compute an integer (x*128 + y)*256 + z and use this as the key.
Also, consider that for all "air", "log", "dirt" blocks you may not need a separate object unless something must be changed at a certain block. Until then, share a single object of a kind.
Cause you just give small piece of code, I can give you two suggestions:
compact the object size. Seems very stupid but very easy to do. Just imagine you have thousands of objects in your memory. If everyone can be compacted half size, you can save half memory :).
Just assign the value to array when you need it. Sometime it is not work if you need really need a assigned array. So just assign values to elements in array as LESS as you can. If you can show me more code, I can help you more.
Are you sure the problem is in this method? Unless Block objects are really big, 256*256*128 ~= 8M objects should not require 1 GB ...
That said, if the blocks do not hold state, it would be more memory efficient to use an enum (or even a byte instead), as we would not need a separate object for each block:
enum Block {
air, grass, dirt, log, unbreakable;
}
Block[][][] map = ...