I'm working on creating an inventory system for a game. The inventory system will use cells and 2D images to represent items, like Minecraft, Diablo, or WoW. I've hit a bit of a snag when trying to let the player place items in the bag (click and drag style).
So far, I've successfully created the inventory grid, which is really smoke and mirrors:
public class InventoryMenu {
boolean objectSelected = false;
Item selectedItem;
public final int COLUMNS = 5;
public final int ROWS = 7;
ArrayList<Item> inv = new ArrayList<Item>((ROWS + 1) * COLUMNS);
Sprite slot;
public void render(Graphics g) {
for (int i = 0; i < 40; i++) {
int col = i % COLUMNS;
int row = ROWS - i / COLUMNS;
slot.draw(g, (Camera.width - slot.getWidth() * COLUMNS) + slot.getWidth()* col, row * slot.getHeight());
if (inv.get(i) != null) {
inv.get(i).render(g);
}
}
if (selectedItem != null) {
selectedItem.render(g);
}
}
Basically, I have an arraylist that can hold items, and it just draws the "slot" image 40 times and if there's an item in that "slot" it draws the item image too. Cool? Cool. The problem comes if I want to allow the player to select an item in their inventory and move it to a different slot. I have no problem letting them pick it up (I use a pretty primitive brute force, but with any reasonably sized inventory, it works):
private Item grabItem(Point2D mouse) {
for (Item i : inv) {
if (i != null) {
if (i.getPhysicsShape().contains(mouse)) {
Item pick = i;
selectedItem = pick;
objectSelected = true;
i = null;
return pick;
}
}
}
return null;
}
That bit of code lets a player pick up an item, but placing it fails - it rarely gets the right slot, except if its the bottom row (0-4):
setDown(){
int slotLoc = calcSlot(InputHandler.mouseCoords);
placeItem(slotLoc);
}
private void placeItem(int loc) {
if(loc < 0 || loc > ROWS * (COLUMNS + 1))
return;
int col = loc % COLUMNS;
int row = ROWS - loc / COLUMNS;
selectedItem.pickUp((Camera.width - slot.getWidth() * COLUMNS) + slot.getWidth() * col, row * slot.getHeight());
inv.set(loc, selectedItem);
selectedItem = null;
objectSelected = false;
}
private int calcSlot(Point2D mouse){
int colPos = (int) (COLUMNS - (Camera.width - mouse.getX()) / slot.getWidth());
int rowPos = (int) (ROWS+1 - (mouse.getY()) / slot.getHeight());
if (colPos > COLUMNS || colPos < 0 || rowPos < 0 || rowPos > ROWS + 1) {
dropItem();
return -1;
} else {
return colPos + 4*rowPos;
}
}
I'm fairly confident that the problem is in calcSlot, but I can't seem to find where. Any help would be greatly appreciated, I'm sure it's something silly.
Images!!
So, I can pick up an item with no problem, and it automatically places it in the last slot. So far, everything is gold.
I can then click that image and lift it out of the grid, and it follows my mouse (mouse is hidden by PrntScrn, but its at the top left corner of the image:
When I try to place the item by clicking in the middle of slot 33, however, it derps and places it, inexplicably, in slot 27.
private int calcSlot(Point2D mouse){
int colPos = (int) (COLUMNS - (Camera.width - mouse.getX()) / slot.getWidth());
int rowPos = (int) (ROWS - ((mouse.getY())- slot.getHeight()) / slot.getHeight());
if (colPos > COLUMNS || colPos < 0 || rowPos < 0 || rowPos > ROWS + 1) {
dropItem();
return -1;
} else {
return COLUMNS*rowPos + colPos;
}
}
The difference % between 33 and 27 is "6"
So i suggest you look very carefully at the fact the numbers and rows run from bottom to top meaning its landed in the "sixth row up" so your linear calculation in the process for which is a row and which is the coord result is getting in the way somewhere in the calculation process (to hazard a guess).
Try placing it in 32 and see what happens for the cell it places.
but also you show the code for calcSlot twice here and in one version it has "ROWS+1"
Related
I wrote algortihm tht checks just part of tic tac toe game board, let's say its 10x10 size.
There's no need to iterate throught whole board every time, just around player move index + game diffuculty in each direction.
Here is my impl:
private boolean checkHorizontalWin(String gameBoard, int gameSize, int gameDifficulty, int moveIdx) {
// CHECK HORIZONTAL WIN
int moveX = moveIdx / gameSize; // 15/10=1
int moveY = moveIdx % gameSize; // 15%10=5
int startX = clamp(moveX - gameDifficulty, 0, gameSize);
int startY = clamp(moveY - gameDifficulty, 0, gameSize);
int endX = clamp(moveX + gameDifficulty, 0, gameSize);
int endY = clamp(moveY + gameDifficulty, 0, gameSize);
for (int row = startY; row < endY; row++) {
char candidate = getPawnAtCoords(gameBoard, gameSize, row, 0);
int counter = 1;
for (int column = startX; column < endX; column++) {
char pawn = getPawnAtCoords(gameBoard, gameSize, row, column);
if ((pawn == candidate) && (pawn != '-')) {
counter++;
} else {
counter = 1;
candidate = pawn;
}
if (counter == gameDifficulty) {
return true;
}
}
}
return false;
}
private int clamp(int val, int min, int max) {
return Math.max(min, Math.min(max, val));
}
private char getPawnAtCoords(String board, int gameSize, int row, int column) {
int index = row * gameSize + column; // 1 * 10 + 5
return board.charAt(index);
}
Algorithm is not working as it's should when player move index is placed on board corner.
Anyone gat an idea?
The problem is that with candidate = getPawnAtCoords(gameBoard, gameSize, row, 0) you read a cell that might not be in the region you want to inspect: the column 0 might not be anywhere near. Yet, you count that cell. So for instance, with a 3-in-a-row game, when you scan the first row where a move was made in column 10, then that row might look like this:
X------XX-X----
^ last move
^ inner loop starts here, but count is already 1 before it starts
^ this X is counted erroneously
Your initialisation of candidate will count the left X and then will find two more X starting at index 7 (i.e. 10 - 3), and conclude there is a 3-in-a-row, but this is a false positive.
You can solve this in many ways. For instance, this will do the trick:
char candidate = '-';
int counter = 0;
Secondly, your encoding of an index (from row/column) is different from the decoding of an index (into row/column). In getPawnAtCoords the column is the smaller unit, and the row the bigger one (it is multiplied), yet in checkHorizontalWin you extract moveX as the bigger unit (as you divide), which is later used for the loop variable column.
You can fix this by changing the initialisation of moveX and moveY to:
int moveX = moveIdx % gameSize;
int moveY = moveIdx / gameSize;
Finally, this is an overly complex algorithm. You already know what the last move was, as you can read gameBoard[moveX] and it is that character you want to check with, nothing else. So the variable candidate could be set to it -- only once. Then you don't need that pawn == '-' check anymore and only need to reset the counter to 0 when you find a character that is different from candidate.
Moreover, for horizontal wins it is not necessary to look at any other row than the row of the last move.
Algorithm is required for Finding the group of 1s in a matrix, but the group of 1s should contain only vertical entry
This is more in the brain-teaser category.
For small matrices, we just access it column by column.
Assuming input is a 2 dimension array of integer, and output is a list of the following class:
class GroupIdentifier {
int col;
int rowStart;
int colStart;
/* the corresponding getter/setter/constructor etc */
}
Here is the function you are looking for:
int FIRST_ONE = 2;
int IN_GROUP = 1;
int OUT_GROUP = 0;
public List<GroupIdentifier> findVerticalGroupsOf1 (int[][] a,
int numRow, int numCol) {
List<GroupIdentifier> answer = new ArrayList<GroupIdentifier>();
for (col = 0; col < numCol; col = col + 1) {
// let's create the tempArr holding the current col
int[] tempArr = new int[numRow];
for (row = 0; row < numRow; row = row + 1 =) {
tempArr[row] = a[row][col];
}
// let's find the groups
// first, init the state
int state = (tempArr[0] == 1) ? FIRST_ONE : OUT_GROUP;
int start = (state == FIRST_ONE) ? 0 : -1;
// I see this problem as a simple state-machine
// We have three states: FIRST_ONE encountered (0 to 1),
// still tracking the 1s IN_GROUP ... (1 to 1) and
// get OUT_GROUP (1 to 0).
// the switch case in the following loop does exactly that.
// So, whenever we get an OUT_GROUP event, we get an answer.
for (int i = 1; i < numRow; i = i + 1) { // edit: changed to numRow as
// it was a typo error
switch (state) :
case FIRST_ONE :
if (tempArr[i] == 0)
state = OUT_GROUP;
else
state = IN_GROUP;
break;
case IN_GROUP :
if (tempArr[i] == 0) {
GroupIdentifier gi = new GroupIdentifier (col, start, i - 1);
answer.add(gi);
}
break;
case OUT_GROUP :
if (tempArr[i] == 1) {
start = i;
state = FIRST_ONE;
}
break;
}
}
// since this question looks like homework,
// i will leave out the boundary case handling
// here. it's not that hard; just copy/paste the
// switch statement and fondle around.
return answer;
}
As a computer scientist myself, I think of ways to optimize it.
For larger matrices, what I would do is to precompute the all combinations of tempArr and store them as Integer --> List map. Then, I will traverse the columns without computing again.
I am trying to save the method outOfBounds which is called inside the lengthOfColor method more than once to a local variable, so that less processing power is used. I provided the lengthOfColor method in which I want to store the variable, and I also provided the outOfBounds method. As you can see the outOfBounds method is a boolean and I am not sure how to store it with integer parameters.
private Integer[] lengthOfColor(int col, boolean color, int pattern, int row) {
int x = 0;
int y = 0;
if (pattern == 1) {
// vertical pattern
y = 1;
} else if (pattern == 2) {
// horizontal pattern
x = 1;
} else if (pattern == 3) {
// diagonal slope left pattern
x = 1;
y = 1;
} else {
// diagonal slope right pattern
x = 1;
y = -1;
}
// length = how many neighbor slots are of same color
// possible equals number of slots, that you can play off of.
// whichSide = left or right if horizontal and top or bottom if vertical.
int length = 0;
int possible = 0;
Integer[] whichSide = new Integer[]{1, -1};
for (int side : whichSide) {
int i = 1;
boolean complete = false;
//while complete is false continue the loop
while (!complete) {
//mainX == horizontal pattern distance
//mainY == vertical pattern distance
int mainX = x * i * side;
int mainY = y * i * side;
//if still inbounds and if the same slot is filled and it matches the color, increment length
if (!outOfBounds(col, mainX, mainY, row) && getIsFilled(col, mainX, mainY, row) &&
checkColor(col, mainX, mainY, row) == color)
{
length++;
}
//if still inbounds and if the same slot is empty, increment possible number of spots and change complete to true
else if (!outOfBounds(col, mainX, mainY, row) && !getIsFilled(col, mainX, mainY, row) &&
getLowestEmptyIndex(myGame.getColumn(col + mainX)) == getLowestEmptyIndex(myGame.getColumn(col)) + mainY - row)
{
possible++;
complete = true;
}
//finish the statement to avoid a infinite loop if neither conditions are met.
else
{
complete = true;
}
// If not complete, then check one slot further.
i = i + 1;
}
}
return new Integer[] {length, possible};
}
private boolean outOfBounds(int col, int x , int y, int row)
{
int currentX = col;
int currentY = getLowestEmptyIndex(myGame.getColumn(col)) - row;
return currentX + x >= myGame.getColumnCount() || currentY + y >= myGame.getRowCount() || currentX + x < 0 || currentY + y < 0;
}
I see that mainX and mainY change values so there isn't any real optimization that can be done outside of the for and while loop besides creating a boolean value that holds the result of outOfBounds before the if check is called which would reduce the number of operations you need to do. To be honest, the optimization is so insignificant that it wouldn't really matter but would be good coding practice I suppose (JIT might optimize for you as well depending on your code). More importantly the method reduces the extra lines of code you need to type and does not necessarily mean that there is less computing.
So something like this before any outOfBounds call but inside the while loop,
boolean outOfBounds = outOfBounds(col, mainX, mainY, row);
and change your current if(!outOfBounds(col, mainX, mainY, row) && ....) into if (!outOfBounds && ...)
Also the #1 rule to optimization is to not optimize until you are done with your project and notice a significant performance dip. In which case you would start with the biggest bottleneck until the optimal performance is gained. Of course this does not mean coding in an incorrect way which would of course create unnecessary performance losses. In those cases it would also be wise to consider whether or not you are looking at the problem the right way rather than micro-optimizing.
Here's a snippet of what I would do to micro-optimize the code shown.
private Integer[] lengthOfColor(int col, boolean color, int pattern, int row) { // consider changing Integer[] into
// int[] if you don't need a boxed integer. It will increase performance
int x = 0;
int y = 0;
// length = how many neighbor slots are of same color
// possible equals number of slots, that you can play off of.
// whichSide = left or right if horizontal and top or bottom if vertical.
int length = 0;
int possible = 0;
switch (pattern) { // switch may be a tad faster but insignificant. More importantly it provides clarity.
case 1:
y = 1;
break;
case 2:
x = 1;
break;
case 3:
x = 1;
y = 1;
break;
default:
x = 1;
y = -1;
break;
}
//int[] whichSide = new int[]{1, -1}; // changed to int[] because you don't need a boxed primitive from what is
// shown
// nevermind, this line isn't needed and you will be able to avoid an instantiation.
for (int i = 1; i != -3; i-=2) {
int count = 1;
int mainX; // bring this to a higher scope. (honestly this is micro optimization but a habit of mine if this is
// can be considered in scope)
int mainY;
boolean outOfBounds = false;
//boolean complete = false; // removed as its unnecessary to break out of the while loop.
//while complete is false continue the loop
while (true) {
//mainX == horizontal pattern distance
//mainY == vertical pattern distance
mainX = x * count * i;
mainY = y * count * i;
outOfBounds = outOfBounds(col, mainX, mainY, row);
//if still inbounds and if the same slot is filled and it matches the color, increment length
if (!outOfBounds && getIsFilled(col, mainX, mainY, row) &&
checkColor(col, mainX, mainY, row) == color) {
length++;
}
//if still inbounds and if the same slot is empty, increment possible number of spots and change complete to
// true
else if (!outOfBounds && !getIsFilled(col, mainX, mainY, row) &&
getLowestEmptyIndex(myGame.getColumn(col + mainX)) == getLowestEmptyIndex(myGame.getColumn(col)) + mainY -
row) {
possible++;
break;
}
//finish the statement to avoid a infinite loop if neither conditions are met.
else {
break;
}
// If not complete, then check one slot further.
count++;
}
}
return new Integer[]{length, possible}; // once again consider whether or not you need a boxed integer
}
private boolean outOfBounds(int col, int x, int y, int row) {
//int currentX = col; this is an unnecessary line
int currentY = getLowestEmptyIndex(myGame.getColumn(col)) - row;
return col + x >= myGame.getColumnCount() || currentY + y >= myGame.getRowCount() || col + x < 0 ||
currentY + y < 0;
}
i am a high school student in an AP Computer Science course, and over the weekend we were assigned this project:
"Simulate the wandering of an intoxicated person in a square street grid. Draw a grid of 10 streets horizontally and 10 streets vertically. Represent the simulated drunkard by a dot, placed in the middle of the grid to start. For 100 times, have the simulated drunkard randomly pick a direction (east, west, north, south), move one block in the chosen direction, and draw the dot. After the iterations, display the distance that the drunkard has covered. (One might expect that on average the person might not get anywhere because the moves to different directions cancel one another out in the long run, but in fact it can be shown with probability 1 that the person eventually moves outside any finite region."
However, because graphics are not part of the curriculum, the grid is simulated by a grid created by a loop in a Grid class, and the dot representing the drunkard is an X, such as :
The default position for the X is at (5,5) which is shown above. However, I am having trouble with getting the X to move randomly.
My Drunkard class
public class Drunkard
{
int row;
int column;
public Drunkard()
{
row = 5;
column = 5;
}
public int getCol()
{
return column;
}
public int getRow()
{
return row;
}
public void moveRandomly()
{
double directionDeterminer = Math.random();
if (directionDeterminer >= 0 && directionDeterminer <= 0.25)
{
row++;
}
else if (directionDeterminer >= 0.25 && directionDeterminer <= 0.50)
{
row--;
}
else if ( directionDeterminer >= 0.50 && directionDeterminer <= 0.75)
{
column++;
}
else if ( directionDeterminer >= 0.75 && directionDeterminer <= 1.00)
{
column--;
}
}
}
and my grid class (Which contains the loop for creating the grid):
public class Grid
{
public static final int MAX_NUM_ROWS = 10;
public static final int MAX_NUM_COLUMNS = 10;
public Grid()
{
}
public void draw(Drunkard theDrunk)
{
Drunkard drunk = new Drunkard();
drunk.getRow();
int y = drunk.getCol();
String newRow = "- - - - - - - - - - ";
drunk.moveRandomly();
for (int row = 0; row < MAX_NUM_ROWS - 1; row++)
{
if (row == 4)
{
y = 8;
System.out.print( newRow.substring(0,y) + "X " + newRow.substring(10,20) );
System.out.print("\n");
}
for (int column = 0; column < MAX_NUM_COLUMNS ; column++)
{
System.out.print("- ");
}
System.out.print("\n");
}
}
}
The method moveRandomly() is supposed to either increment or decrement the rows or columns so that the position of the X changes either north, south, east or west. However, i am unsure on how to have the variables from moveRandomly() (row and column) have any effect on the grid created in the Grid class. Does anyone have an idea on how to make it so the variables have an effect on the grid? Keep in mind that I am a beginning programmer, so I have basic knowledge on loops and if statements, not arrrays or graphics. Any comments would be greatly appreciated.
It looks pretty good, i think you might just want something like this in draw:
for(int row = 0; row < MAX_NUM_ROWS - 1; row ++)
{
for(int column = 0; column < MAX_NUM_COLUMNS - 1; column ++)
{
if((row == theDrunk.getRow()) && (column == theDrunk.getCol()))
System.out.print("X");
else
System.out.print("-");
}
System.out.println();
}
This could also be done with substring given some modification
I'm trying to make the game Tetris in java.
I've gotten it to the point where:
a new block is generated when it hits the floor or its y+1 is not null (meaning there's another block under it)
public void collisionCheck(int x, int y) {
if (activetile.getY() == this.height-2 || getTileAt(x, y+1) != null) {
activetile = new Tile(this, 0, 0);
}
}
A row clears when the bottom row is full of non-null values, or the Tetris pieces (for y = 4 (the floor), loop through x till x = 4 and check if all non-null)
public void checkBottomFull(int x, int y) {
while (getTileAt(x,y) != null) {
say("(" + x + ", " + y +")");
if (x == 3) {
say("row is full");
//replace full row with tiles from above
for (int i = 0; i < 4; i++) {
for (int j = 5; j > 0; j--) {
grid[j][i] = getTileAt(i,j-1);
grid[j-1][i] = null;
}
}
break;
}
x++;
}
}
Right now, I'm using keys to move the block:
public void keyPressed(KeyEvent e) {
int keyCode = e.getKeyCode();
if(keyCode == KeyEvent.VK_DOWN) {
activetile.setLocation(activetile.getX(), activetile.getY()+1);
System.out.println("coordinates: " + activetile.getX() + ", " + activetile.getY());
collisionCheck(activetile.getX(),activetile.getY());
checkBottomFull(0,4);
repaint();
}
}
There's two issues I'm having:
1) In the picture you'll notice I've dropped the block all the way to the floor... and the row cleared. After it's cleared, it will generate a block to the top left (x=0, y=1) which I have no control over.
2) On the floor there seems to be a red line... which I'm assuming is a row of blocks hidden by the JFrame... I'm not sure why that's there.
FYI: If you're wondering why grid[j][i] has the rows and columns flipped (aka, why it's not grid[i][j]) is because I instantiated it as grid = new Tile[height][width];
Any thoughts?
Thanks!
It is hard to say what is wrong without actually debugging your app.
But maybe try this one:
public void checkBottomFull(int x, int y) {
while (getTileAt(x,y) != null) {
say("(" + x + ", " + y +")");
if (x == 3) {
say("row is full");
//replace full row with tiles from above
for (int i = 0; i < 4; i++) {
for (int j = 4; j >= 0; j--) {
grid[j][i] = getTileAt(i,j-1);
grid[j-1][i] = null;
}
}
break;
}
x++;
}
}
You have 5 rows (indexed from 0 to 4) and 4 columns (indexed from 0 to 3).
What values of height and width do you pass to:
grid = new Tile[height][width];
Because from what I see you should do something like that:
grid = new Tile[5][4];
Bah,
Turns out in the key event, I needed to check if the bottom was full before checking if there is a collision.
I guess what was happening is, when I was checking collisionCheck(activetile.getX(),activetile.getY()); before checkBottomFull(0,4);, when the bottom was full, it would clear the row and set the current row equal to the row above it: grid[j][i] = getTileAt(i,j-1);, the problem was that collisionCheck was generating a new piece and the that newly generated piece was getting cleared and replaced by checkBottomFull.
Putting the collision check after the checkBottomFull ensures that the newly generated piece won't be replaced if bottom is full.