I am trying to not load my entire tile based map into memory to save RAM client side. The map will be huge and already is requriring 1GB client side (multi-layered map).
I have gotten some perspective on Game Dev SO. I am trying to Load zones/chunks of my game map into memory (i.e. 300x300) and then when the player moves 100 steps shift the array and load 100 new tiles depending on direction. I have tried to work on a scaled version of this and now have a generic question.
I need help when the playerX/Y coordinates are on the perimeter of the map (which causes the chunk to be outside of the map)
Here is what I have come up with so far (note: player is in center of chunk & chunk always odd number sized)... It has the following issues(when the character is on the edge of the map):
change characterX/Y to 0,0 and the bottom left(0,2) coordinate will incorrectly be 7
0, 0, 0
0, 1, 1
7, 1, 8
change characterX/Y to 8,8 and the top right(2,0) coordinate of the chunk will incorrectly be 6
1, 1, 6
1, 1, 0
0, 0, 0
Here is the SSCCE:
public class MapChunkLoad {
public static void main(String[] args) {
short[] groundLayer;
int mapWidth = 9;
int mapHeight = 9;
int chunkWidth = mapWidth / 3; //3
int chunkHeight = mapHeight / 3; //3
int characterX = 8;
int characterY = 8;
String map = "1, 1, 1, 1, 1, 1, 1, 1, 7, " +
"1, 8, 8, 1, 1, 1, 1, 1, 1, " +
"1, 8, 9, 9, 1, 1, 1, 1, 1, " +
"1, 1, 9, 9, 1, 1, 1, 1, 1, " +
"1, 1, 1, 1, 1, 1, 1, 1, 1, " +
"1, 1, 1, 1, 1, 1, 1, 1, 1, " +
"1, 1, 1, 1, 1, 1, 1, 1, 1, " +
"1, 1, 1, 1, 1, 1, 1, 1, 1, " +
"6, 1, 1, 1, 1, 1, 1, 1, 1";
String[] strArr = map.split(", ");
groundLayer = new short[chunkWidth * chunkHeight];
//load chunk into groundLayer
int arrayIndex = 0;
int count = (characterX - (chunkWidth/2)) + ((characterY - (chunkHeight/2)) * mapWidth); //top left tile within chunk
for (int y = 0; y < chunkHeight; y++){
for (int x = 0; x < chunkWidth; x++){
if (count > -1 && count < strArr.length){
groundLayer[arrayIndex] = Short.parseShort(strArr[count]);
System.out.println("arrayIndex[" + arrayIndex + "] = " + strArr[count]);
} else {
groundLayer[arrayIndex] = 0;
System.out.println("arrayIndex[" + arrayIndex + "] = " + 0);
}
arrayIndex++;
count++;
}
count += (mapWidth - chunkWidth);
}
System.out.println("");
//print map grid
int printcount = 0;
for (int y = 0; y < chunkHeight; y++){
for (int x = 0; x < chunkWidth; x++){
if (x == chunkWidth - 1){
System.out.println(groundLayer[printcount]);
} else {
System.out.print(groundLayer[printcount] + ", ");
}
printcount++;
}
}
}
}
Thanks so much for any assistance.
So I think your logic for checking if count is outside the bounds is faulty.
I think it needs to be more complex to account for that your array represents a 2D figure. I suspect for chunks larger than 3x3 you're getting a lot more errors that are really tricky to describe. Consider this map where each square's value is its index.
0 1 2
3 4 5
6 7 8
when in the top right corner (2) your map should look like this
0 0 0
1 2 0
4 5 0
but this will fail because count will in some cases be a valid index when calculating xValue*width+yValue, but you want that to be invalid (mapped to 0). Instead you need to keep track of both the X and Y components of count and make your map display a zero when either of those are out of bounds.
int countX = characterX - (chunkWidth/2);
int countY = characterY - (chunkHeight/2);
int index = countX + (countY*mapWidth)
then later. Instead of checking:
if (count > -1 && count < strArr.length)
check:
if( countX + x >= mapWidth || countY + y >= mapHeight)
EDIT:
As you can imagine this also changes how you count. You will also need a way to break your loop. Something like
if(x == chunkWidth && y == chunkWidth) break;
I would be more specific, but I'm having trouble loading your original post to use as reference.
I think that fixes everything. Leave a comment if you have any questions. Good luck!
I'm not sure if this is what you are looking for, but commonly you'd check for bounds inside your nested for-loop that fills the groundLayer, rather than checking with a count variable. That way will be much more robust. Something like this:
for(int y = 0;y < chunkHeight;y++) {
for(int x = 0;x < chunkWidth;x++) {
//Get the absolute position of the cell.
int cellX = characterX + x - chunkWidth / 2; //Please check if this is correctly lined out.
int cellY = characterY + y - chunkHeight / 2;
if(cellX >= 0 && cellX < mapWidth && cellY >= 0 && cellY < mapHeight) { //Within bounds.
groundLayer[arrayIndex] = Short.parseShort(strArr[cellX + (cellY * mapWidth)]);
} else { //Out of bounds, put down a placeholder.
groundLayer[arrayIndex] = 0;
}
arrayIndex++;
}
}
Let me know if this was what you were looking for, and whether it works!
Related
I have the code below, which makes a staircase in java with a for loop:
for (int i = 0; i < 10; i++) {
g.drawRect(5, 5 + 10 * i, 10 + 10 * i, 10);
}
But I want the same thing, but backwards, upside down, and reversed, like the image below:
Any ideas on the best way to achieve this?
You can represent a staircase as a 2d array of 0 and 1 in some of its corner. Then you can iterate over this array and for each number print a filled or hollow square.
For example, a 2d array with a staircase in the lower right corner:
int m = 5;
int[][] staircase = IntStream.range(0, m)
.mapToObj(i -> IntStream.range(0, m)
//.map(j -> i >= j ? 1 : 0) // lower left
//.map(j -> i <= j ? 1 : 0) // upper right
//.map(j -> i + j < m ? 1 : 0) // upper left
.map(j -> i + j >= m - 1 ? 1 : 0) // lower right
.toArray())
.toArray(int[][]::new);
// output
Arrays.stream(staircase).map(Arrays::toString).forEach(System.out::println);
[0, 0, 0, 0, 1]
[0, 0, 0, 1, 1]
[0, 0, 1, 1, 1]
[0, 1, 1, 1, 1]
[1, 1, 1, 1, 1]
See also: Display squares of asterisks, filled and hollow, side by side
For example, {1, 4, 45, 6, 0, 19} and the number 51 should return 3, because the number of elements in the smallest subarray which together are greater than 51 are 3: {4,45,6}`.
{7, 2, 5, 10, 1} and the number 9 should return 1, because the number of the elements in the smallest subarray possible that is greater than 9 is {10}.
If array is null or empty, or the array has no subarray that is greater than the given number, the method has to return -1.
I'm not allowed to use array package from java.util.
My goal is to execute the method in O(n) time.
This is my code so far, if the array has no subarray greater than the given number, it returns an OutofBounds error.
Anyone has a clue?
public static int smallestSubSum(int arr[], int x) {
int left = 0, right = 1, smallest = 0;
int sum = arr[right];
for (int i = 1; i < arr.length; i++) {
if (sum > x)
smallest = left - right;
else
right++;
sum += arr[right];
if (sum > x && left - right < smallest) {
smallest = left - right;
left++;
} else
sum -= arr[left];
left++;
if (sum > x && left - right < smallest)
smallest = left - right;
}
return smallest;
}
Edit: Perhaps I should explain what I tried to do with my code, basically I wanted the sum to hold the first two elements in the code, and then compare with each 'if' iteration if the sum of the current elements are greater or less than X, if not I raise the right element to go further, if yes I erase the first element, the 'left' one.
The array of {1, 4, 45, 6, 0, 19} and the number 51 returns 2, even though the result should be 3. I don't know why, because my right reaches the index 3 which is 6 and the left one reaches index 1 which is 4, so the result should indeed be {4,45,6} but it doesn't get to it.
This is the best I could do.
Here are my test results.
[1, 4, 45, 6, 0, 19] -> 51
3
[7, 2, 5, 10, 1] -> 9
1
[1, 4, 45, 6, 0, 19] -> 200
-1
I just cycled through the array with a for loop. Whenever the total exceeded the X amount, I subtracted values until the total dropped below the X amount.
Here's the complete runnable code I tested with.
import java.util.Arrays;
public class SmallestSubarray {
public static void main(String[] args) {
int[] arr1 = new int[] { 1, 4, 45, 6, 0, 19 };
int x1 = 51;
System.out.println(Arrays.toString(arr1) + " -> " + x1);
System.out.println(smallestSubSum(arr1, x1));
int[] arr2 = new int[] { 7, 2, 5, 10, 1 };
int x2 = 9;
System.out.println(Arrays.toString(arr2) + " -> " + x2);
System.out.println(smallestSubSum(arr2, x2));
int[] arr3 = new int[] { 1, 4, 45, 6, 0, 19 };
int x3 = 200;
System.out.println(Arrays.toString(arr3) + " -> " + x3);
System.out.println(smallestSubSum(arr3, x3));
}
public static int smallestSubSum(int arr[], int x) {
if (arr == null || arr.length < 1) {
return -1;
}
int sum = 0;
int minCount = Integer.MAX_VALUE;
int index = 0;
for (int i = 0; i < arr.length; i++) {
sum += arr[i];
while (sum > x) {
minCount = Math.min(i - index + 1, minCount);
sum -= arr[index];
index++;
}
}
return (minCount == Integer.MAX_VALUE) ? -1 : minCount;
}
}
I'm studying the Conway's Game of Life to implement it on my own, and came across the following implementation with the rules:
Given a board with m by n cells, each cell has an initial state live (1) or dead (0). Each cell interacts with its eight neighbors (horizontal, vertical, diagonal) using the following four rules (taken from the above Wikipedia article):
Any live cell with fewer than two live neighbors dies, as if caused by under-population.
Any live cell with two or three live neighbors lives on to the next generation.
Any live cell with more than three live neighbors dies, as if by over-population..
Any dead cell with exactly three live neighbors becomes a live cell, as if by reproduction.
And implementation (https://discuss.leetcode.com/topic/29054/easiest-java-solution-with-explanation):
public void gameOfLife(int[][] board) {
if (board == null || board.length == 0) return;
int m = board.length, n = board[0].length;
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
int lives = liveNeighbors(board, m, n, i, j);
// In the beginning, every 2nd bit is 0;
// So we only need to care about when will the 2nd bit become 1.
if (board[i][j] == 1 && lives >= 2 && lives <= 3) {
board[i][j] = 3; // Make the 2nd bit 1: 01 ---> 11
}
if (board[i][j] == 0 && lives == 3) {
board[i][j] = 2; // Make the 2nd bit 1: 00 ---> 10
}
}
}
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
board[i][j] >>= 1; // Get the 2nd state.
}
}
}
public int liveNeighbors(int[][] board, int m, int n, int i, int j) {
int lives = 0;
for (int x = Math.max(i - 1, 0); x <= Math.min(i + 1, m - 1); x++) {
for (int y = Math.max(j - 1, 0); y <= Math.min(j + 1, n - 1); y++) {
lives += board[x][y] & 1;
}
}
lives -= board[i][j] & 1;
return lives;
}
And driver:
public static void main(String args[]) {
GameOfLife gl = new GameOfLife();
int[][] board = {
{0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 1, 0, 0, 0, 0, 0},
{0, 1, 0, 1, 0, 0, 0, 0, 0},
{0, 0, 1, 1, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0}
};
gl.gameOfLife(board);
}
And my question is, what do the x and y in liveNeighbors() represent? Do not understand why the need for Math.min() and Math.max(). And also, does lives represent the amount of initialized lives on the board?
The given code is using the min and max functions to limit the search to valid entries in the array. If this is not done, the code will return an ArrayOutOfBoundsException when trying to use -1, m, or n as array indexes. (The loop doesn't "know" that given a square at the right edge of the map, it shouldn't search for living neighbors further to the right; these functions encode that fact.) x and y are simply the loop control variables which are used to iterate over valid squares surrounding the target square.
As for the lives variable, that's the placeholder to keep count of how many live neighbors have been found by the loops below. You might have guessed this by the fact that it's the return value of the liveNeighbors function.
Let's do an example. We'll call liveNeighbors(board,9,9,0,2), where board is the board given in the driver. Your board has dimensions 9x9, so those are the m and n we pass, and for our example we're investigating the square at 0,2, which is the first entry in the third row (which has a 1 to its right). Great, let's begin.
i=0, so x = Math.max(i - 1, 0) = Math.max(-1, 0) = 0 (this shows the reason for the max function: if we just said int x=i-1, we would end up with x = -1 which is out of the bounds of the array. Next we evaluate x <= Math.min(i + 1, m - 1) = Math.min(1, 8) = 1. If we were investigating a cell in the final column, this condition would have enforced the right edge of the array.
I'll leave the similar logic involving y and j to you.
The loop simplifies to:
for (int x = 0; x <= 1; x++) {
for (int y = 1; y <= 3; y++) {
lives += board[x][y] & 1;
}
}
The inner loop will run six times, with the following (x,y) pairs: (0,1),(0,2),(0,3),(1,1),(1,2),(1,3). Convince yourself that these are the neighbors of the square we're investigating, as well as the square itself.
Five of these six squares will return 0, with the one at (1,2) returning 1, so at the end of this loop, lives will equal 1. The final thing to do is lives -= board[i][j] & 1;, which reduces lives by 1 if the square we're investigating has a 1 in it. In our case it doesn't (board[i][j] = 0) so we subtract 0, leaving us with 1, which we return. liveNeighbors(board,9,9,0,2) = 1
I may have gotten x and y backwards once or twice, but hopefully that's enough so you can understand what's going on.
I am really stuck at this problem.
In this problem, you are given a 2xN board. You need to fill in non-negative numbers in this board in such a way, that:
The sum of all the numbers filled = N
Each of the 2 rows
consist of numbers in non-increasing order
Each of the N
columns consist of numbers in non-increasing order.
In how many ways can this be done, given the number N?
Two ways are considered different if there is a cell in the board which has different numbers.
The output should be the number of ways the matrix can be formed.
The matrix can have repetitive numbers and zero can be used. The matrix should not have increasing numbers but equal numbers can be filled along side each other.
Example:
input-> 5
output->16
From your example (input=5, output=16) I suppose only integer numbers are allowed.
One naive (brute force) solution is to use backtracing algorithm:
http://en.wikipedia.org/wiki/Backtracking
On this site you can see example with sudoku board being filled until solution is found.
==
For example:
You have array of integers with size 2N.
For position 0 you take first free number.
If solution is not broken yet you go to position 1 of array.
If solution is broken - stop as cannot back anymore
For position 1 you take next free number.
If solution is not broken you you go to position 2 of array.
If solution is broken you back to previous sten and take next free number.
For position 2...
This is typically done with recursion.
I think, on each position (recursion level) numbers can be taken from pool 0..N.
Try - good luck.
EDIT:
Here is valid solution (using backtracking algo):
private final int N = 5;
// 2 rows in one array [0..N-1, N..2N-1]
private int[] board = new int[2 * N];
// found solution counter
int found = 0;
/*
* this method set next number to current position
* and recursively go to next position.
*/
public void check(int position) {
// if board is complete - check if valid
if (position == 2 * N) {
if (isValid()) {
System.out.println("foun : " + Arrays.toString(board));
found++;
}
return;
}
// if board is not complete - put all numbers (0..N) into current position
// and recursively go to next position
for (int v = 0; v <= N; v++) {
board[position] = v;
// if solution is already broken - step backwards
// see: backtracking algorithms
if (isBroken(position)) {
return;
}
check(position + 1);
}
}
public boolean isValid() {
// condition 1
int sum = 0;
for (int i = 0; i < board.length; i++) {
sum += board[i];
}
if (sum != N) {
return false;
}
// conditin 2
int prev = board[0];
for (int i = 1; i < N; i++) {
if (board[i] > prev) {
return false;
}
prev = board[i];
}
prev = board[N];
for (int i = N + 1; i < 2 * N; i++) {
if (board[i] > prev) {
return false;
}
prev = board[i];
}
// condition 3
for (int i = 0; i < N; i++) {
int top = board[i];
int bottom = board[i + N];
if (top < bottom) {
return false;
}
}
// valid
return true;
}
// simplified version of this method - but correct
public boolean isBroken(int current) {
int sum = 0;
for (int i = 0; i <= current; i++) {
sum += board[i];
}
return sum > N;
}
public void start() {
check(0);
System.out.println("found: " + found);
}
And program output for N = 5:
found : [1, 1, 1, 0, 0, 1, 1, 0, 0, 0]
found : [1, 1, 1, 1, 0, 1, 0, 0, 0, 0]
found : [1, 1, 1, 1, 1, 0, 0, 0, 0, 0]
found : [2, 1, 0, 0, 0, 1, 1, 0, 0, 0]
found : [2, 1, 0, 0, 0, 2, 0, 0, 0, 0]
found : [2, 1, 1, 0, 0, 1, 0, 0, 0, 0]
found : [2, 1, 1, 1, 0, 0, 0, 0, 0, 0]
found : [2, 2, 0, 0, 0, 1, 0, 0, 0, 0]
found : [2, 2, 1, 0, 0, 0, 0, 0, 0, 0]
found : [3, 0, 0, 0, 0, 2, 0, 0, 0, 0]
found : [3, 1, 0, 0, 0, 1, 0, 0, 0, 0]
found : [3, 1, 1, 0, 0, 0, 0, 0, 0, 0]
found : [3, 2, 0, 0, 0, 0, 0, 0, 0, 0]
found : [4, 0, 0, 0, 0, 1, 0, 0, 0, 0]
found : [4, 1, 0, 0, 0, 0, 0, 0, 0, 0]
found : [5, 0, 0, 0, 0, 0, 0, 0, 0, 0]
found: 16
I'm almost positive this has a solution in closed form, or its similar to another problem with a solution in closed form. I would do it programmatically with recursion.
So suppose you know a solution for N. You want N+1. So what you should do is take all the solutions for N and see where you can stick an extra 1 in there without breaking any constraints. That is, super impose the N solution(s) on the N+1 board then try in all 2N places to add 1 without breaking constraints. Then store all of them in a set so they will be deduped.
In any case, its similar to http://en.wikipedia.org/wiki/Partition_(number_theory)
Here's a brute force way: since this is a 2xN matrix and the sum of all numbers must be N, the simplest solution is to fill the first row with 1's, and the 2nd row with 0's. Now you will need a recursive algorithm that takes a valid board, and removes 1 from any "free" position and add it to any legal position. That board is also a solution. By "free" I mean a number n at position [i, j] where [i+1, j] <= n - 1 and [i, j + 1] <= n - 1. You then recursively invoke the algorithm on the new boards, and save everything.
All that's left is to deduplicate solutions.
Example of the algorithm on input 5:
Initial solution:
11111
00000
The only "free" number is [0, 4]. Remove 1, the only legal positions are [0, 0] and [0, 1]. This gives you 2 new solutions
21110
00000
and
11110
10000
Now apply same algorithm again on both these solutions. Notice that the 2nd board now has 2 "free" numbers. Repeat until you get to
50000
00000
EDIT: Just had a lot of fun coding this example. Didn't test it, but that's where my head is going:
public void TwoRowBoard()
{
var board = new int[2, N];
//Create initial, simplest solution.
for (int i = 0; i < N; i++)
{
board[0, i] = 1;
}
var solutions = new List<int[,]>();
RecursiveSolve(board, solutions);
}
private void RecursiveSolve(int[,] board, List<int[,]> solutions)
{
var freeNumbers = GetFreeNumbers(board);
foreach (var freeNumber in freeNumbers)
{
board[freeNumber.i, freeNumber.j] -= 1;
var legalPositions = GetLegalPositions(board);
foreach (var legalPosition in legalPositions)
{
var newBoard = Copy(board);
newBoard[legalPosition.i, legalPosition.j] += 1;
solutions.Add(newBoard);
RecursiveSolve(newBoard, solutions);
}
}
}
private List<Coordinates> GetLegalPositions(int[,] board)
{
//Position 0, 0 is always legal.
var results = new List<Coordinates> {new Coordinates {i = 0, j = 0}};
//Row 0
for (int j = 1; j < N; j++)
{
if (board[0, j - 1] > board[0, j])
{
results.Add(new Coordinates{i = 0, j = j});
}
}
//Row 1. Board[1, higher than N/2] are never legal positions.
for (int j = 0; j <= N /2; j++)
{
if (board[1, j - 1] > board[1, j]
&& board[0, j] > board[1, j])
{
results.Add(new Coordinates{i = 1, j = j});
}
}
return results;
}
private List<Coordinates> GetFreeNumbers(int[,] board)
{
var results = new List<Coordinates>();
for (int i = 0; i < 2; i++)
{
for (int j = 0; j < N; j++)
{
if (i == 0 && j == 0)
{
continue;
}
if (i == 0)
{
if (j == N - 1 && board[0, j] > 0)
{
results.Add(new Coordinates {i = 0, j = j});
}
else if (board[0, j] > board[1, j]
&& board[0, j] > board[0, j + 1])
{
results.Add(new Coordinates {i = 0, j = j});
}
}
else
{
if (j > N/2 && board[1, j] > 0)
{
throw new Exception("Don't see how it's possible for board[1, N/2 or higher] to not be 0");
}
if (board[1, j] > board[1, j + 1])
{
results.Add(new Coordinates{i = 1, j = j});
}
}
}
}
return results;
}
public class Coordinates
{
public int i { get; set; }
public int j { get; set; }
}
I am having a problem programming the below problem in java it is a constraint satisfaction problem:
If I have constraints like this:
x1 + x2 > x3
x2 - x4 = 2
x1 + x4 < x5
Each of x1 to x5 are in the domain {0,1,2}
How do I program the different combinations such that I will have a set of tuples as: {(0,0,0), (0,0,1), (0,1,0),(0,1,1),(1,0,0), ......} for each constraint
that is constraint 1 for instant has domain of tuple such as {(0,0,0), (0,0,1), (0,1,0),(0,1,1),(1,0,0),(0,1,2),(2,0,1) ......}
I need the reply in any language but preferably java please.
You could perhaps do this through the use of some helper methods from the google commons collect library. It would look something like this:
I'm assuming that the tuples (0,0,0) etc are tuples of the input to the constraint, (x0, x1, x2) for constraint1, (x2, x4) for constraint2 etc.
So, for constraint1, first we fill a list with all possible combinations:
final List<int[]> allCombos = new ArrayList<int[]>();
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
for (int k = 0; k < 3; k++) {
allCombos.add(new int[] {i, j, k});
}
}
}
for (final int[] i : allCombos) {
System.out.println(i[0] + ", " + i[1] + ", " + i[2]);
}
Next, we want to filter so we'll be left with the tuples that are allowed by constraint1:
final List<int[]> constraint1 = ImmutableList.copyOf(Iterables.filter(allCombos, new Predicate<int[]>() {
#Override
public boolean apply(#Nullable final int[] input) {
return input[0] + input[1] > input[2];
}
}));
for (final int[] i : constraint1) {
System.out.println(i[0] + ", " + i[1] + ", " + i[2]);
}
This might need a little explanation.
ImmutableList.copyOf is a method that creates a copy of a given list. To this method, we pass the result of Iterables.filter(), which takes a list (the input to be filtered), and a Predicate, which has an overridden method apply(), where you decide which element of the input list that are supposed to be part of the result list. Here, we basically just code the constraint itself, and the cases where the apply method returns true will be part of the filtered list. (I've chosen to represent the tuples as an array, you could use the filter-strategy with any tuple-representation..)
The result of the last printouts (the filtered list) will be:
0, 1, 0
0, 2, 0
0, 2, 1
1, 0, 0
1, 1, 0
1, 1, 1
1, 2, 0
1, 2, 1
1, 2, 2
2, 0, 0
2, 0, 1
2, 1, 0
2, 1, 1
2, 1, 2
2, 2, 0
2, 2, 1
2, 2, 2
I'll leave it up to you to do the same for the other constraints..