Randomising level layout (AS3/Flashpunk) - actionscript-3

I'm cutting my teeth on randomization in games and trying to figure out the trick that games like Binding Of Isacc & Sperlunky use of pre-fab level fragments arranged randomly to create a functional floor/level, something along the lines of this:
A floor will always contain the player, exit and a bonus room (yellow dot). Rooms that are connected are directly connected to each other.
I have a faint idea of how to go about it: Start by creating an multi-dimensional array that holds the tilemap & grid data:
public static var levelLayout:Array =
[[tile,grid],[tile,grid],[tile,grid],[tile,grid]
[tile, grid],[tile,grid],[tile,grid],[tile,grid] etc]
And from there, go through each grid space, roll to see if that spot is nothing, also making rooms nothing if they are isolated and then begin to assign rooms from an shuffled array that contains all the tilemaps/grids.
Am I on the right track? How should I handle the exits? Any help would be appreciated. Cheers

Start by making a perfect maze (a maze where you can get to any point in the maze from anywhere else in the maze). This helps create a network of rooms that are connected in a winnable/solvable way.
There are plenty of tutorials out there for doing this. This one is a little dirty and has a bunch of code you may not need, but it's good to look through some relevant examples of this to understand different approaches:
http://www.emanueleferonato.com/2008/11/28/perfect-maze-generation-with-as3/
I've had good luck with a recursive backtracking algorithm:
http://en.wikipedia.org/wiki/Maze_generation_algorithm#Recursive_backtracker
Consider each cell in the maze to be a room. Once the maze is built, you know which rooms are connected to other rooms, so make each room have a means for either having a door to connect to the next room, or a wall. You probably would want to make like a 4x4 perfect maze.
Ways to spice it up-
You can safely remove deadends from your maze and it will still be perfect. Once the maze is generated, loop through all cells in the maze. Any cell that has 3 walls is a dead end, and you can wall it off completely. Or make it a secret room or whatever.
Add loops to the maze. Randomly connect 2 rooms in the maze that weren't previously connected. This still keeps the maze perfect, it just makes it a little more open.
This is fun territory, enjoy it :)

I've actually been looking into information like this myself recently, having been inspired by Spelunky and Rogue Legacy. While reading around, I found there is a lot of information on how the level layout in Spelunky is generated at http://tinysubversions.com/spelunkyGen/
Inspired(stolen?) by this, I've rigged up a simple version of this to generate a multidimensional Array with a single solution path, using the numbers 0-5 to identify what type of room each cell represents (based off the site listed above). Note that you could easily have it return a different type of result based on your needs. Also I've used Math.random() throughout but you could substitute FP.rand() if you wanted. Supports level layouts of whatever size Room x Room, default is 4 x 4. Some random results I've had [EDITED TO MAKE MORE READABLE]:
4 x 4 6 x 5 7 x 12
[S][ ][ ][ ] [ ][ ][ ][ ][ ][S] [v][-][-][-][-][-][S]
[v][ ][ ][ ] [ ][ ][v][-][-][^] [^][-][-][-][-][-][v]
[^][-][-][v] [ ][v][^][ ][ ][ ] [ ][ ][v][-][-][-][^]
[E][-][-][^] [v][^][ ][ ][ ][ ] [v][-][^][ ][ ][ ][ ]
[^][-][-][-][-][E] [v][ ][ ][ ][ ][ ][ ]
[^][-][-][-][-][-][v]
[ ][ ][v][-][-][-][^]
[v][-][^][ ][ ][ ][ ]
[v][ ][ ][ ][ ][ ][ ]
[^][-][-][-][-][-][v]
[ ][ ][ ][ ][ ][ ][v]
[E][-][-][-][-][-][^]
In any case, I think it's better suited for the 4x4 layout of Spelunky (gee, I wonder why) and I'm pretty sure that I'm going about it slightly off, but here's my code:
/**Generates a pseudo-random single solution path layout to be used for tile painting
* #param dimenR Number of rows (Y-Axis) Should correspond with height of level, in rooms
* #param dimenC Number of columns (X-Axis) Should correspond with width of level, in rooms
* #return The resulting multi-dimensional array*/
private function generateNewRoomLayout(dimenC:int = 4, dimenR:int = 4):Array {
// NOTE: 0= non solution, 1= left/right, 2= left/right/down, 3= left/right/up, 4= start, 5= end
// Initialize array as all non-solution path rooms
var generatedLayout:Array = new Array(dimenR);
for (var i:int = 0; i < dimenR; i++) {
generatedLayout[i] = new Array(dimenC);
for (var j:int = 0; j < dimenC; j++) { generatedLayout[i][j] = 0; }
}
// Initialize our Start Room location
/**Position along X-Axis in the grid*/
var column:int = Math.random() * dimenC;
/**Position along Y-Axis in the grid*/
var row:int = 0;
generatedLayout[row][column] = 4;
/**Chooses the next direction. 0-1= left, 2-3= right, 5= down*/
var chooseDirection:int = Math.random() * 6;
/**Direction solution path is currently taking. -1= left, 1= right*/
var currentDirection:int = 0;
// Keep running until our position goes beyond the dimension of the grid
while (row < dimenR) {
// Chosen to go downards
if (chooseDirection == 5) {
// Reset which way we're going so we can make a random choice later
currentDirection = 0;
if (generatedLayout[row][column] != 4) { generatedLayout[row][column] = 2; }
// Bottom row
if (row == dimenR - 1) {
generatedLayout[row][column] = 5; // Found our ending point
break;
}
else {
row++;
generatedLayout[row][column] = 3;
}
}
else {
// If we don't have a direction to go left/right
if (currentDirection == 0) {
if (chooseDirection < 3) { currentDirection = -1; }
else { currentDirection = 1; }
}
// If we're at the edge of the grid and trying to move past it, we move down and flip our direction instead
if ((currentDirection < 0 && column == 0) || (currentDirection > 0 && column == dimenC - 1)) {
currentDirection = -currentDirection;
generatedLayout[row][column] = 2;
if (row == dimenR - 1) {
generatedLayout[row][column] = 5;
break;
}
else {
row++;
generatedLayout[row][column] = 3;
}
}
else {
column += currentDirection;
generatedLayout[row][column] = 1;
}
}
chooseDirection = Math.random() * 6;
}
return generatedLayout;
}

Related

AS3: adding different numbers in an array to get specific result.

I got a numberArray.
It contains intergers - randomised, within a specific range.
I want to get a specific sum, but not for everything inside the numberArray,
more of trying to sum up different amount of numbers (total of 5 only) inside the numberArray and see if it'll get the specific total required. and if not, it'll randomise another number to take over one of the numbers inside the numberArray.
What's the easiest way to do this ?
doing lots of
if (numberArray[1] + numberArray[2] == specificNumber)
{
}
if (numberArray[1] + numberArray[3] == specificNumber)
{
}
etc. etc. etc.
have too many lines of codes, and it seems like there are easier codes. right now i only have 5 different numbers in the array, so it's still bearable, but if the amount of numbers are higher.... ....
Reading your question like this: For your array of random integers, find a (or all) set(s) of integers that have a given sum.
This is an NP-Complete problem - i.e. there's no known algorithm that solves it efficiently.
The fastest known way is rather complex, so we'll go with a naive solution - should be good enough if you're not doing this on every frame or the input set is huge.
This should also work with 0 or negative values in the input set.
// The sum we're looking for:
var requiredSum:int = 8;
// Our input set:
var numberArray:Array = [1, 2, 3, 4, 5, 2, 3];
// Results will be stored here:
var resultSets:Array = [];
// Go through all possible subset sizes.
// This allows subset sizes all the way up to the size of
// the input set (numberArray.length).
// You can modify it to a fixed value (say, 5), of course:
for (var subsetSize:int = 1; subsetSize <= numberArray.length; subsetSize++)
{
// We'll use the same array for all our attempts of this size:
var subset:Array = new Array(subsetSize);
findSum(numberArray, subset, 0, 0);
}
// Output results:
for (var i:int = 0; i < resultSets.length; i++)
{
trace(resultSets[i].join("+"));
}
// numberArray : Our input set
// subset : The set we're currently filling
// setIndex : The position we're at in numberArray
// subsetIndex : The position we're at in the set we're filling
function findSum(numberArray:Array, subset:Array, setIndex:int,
subsetIndex:int):void
{
// Try every value from the input set starting from our current position,
// and insert the value at the current subset index:
for (var index:int = setIndex ; index < numberArray.length; index++)
{
subset[subsetIndex] = numberArray[index];
// Have we filled the subset?
if (subsetIndex == subset.length - 1)
{
var sum:int = 0;
for (var i:int = 0; i < subset.length; i++)
{
sum += subset[i];
}
if (sum == requiredSum)
{
// Clone the array before adding it to our results,
// since we'll be modifying it if we find more:
resultSets.push(subset.concat());
}
}
else
{
// Recursion takes care of combining our subset so far
// with every possible value for the remaining subset indices:
findSum(numberArray, subset, index + 1, subsetIndex + 1);
}
}
}
Output for the values used in the above code:
3+5
5+3
1+2+5
1+3+4
1+4+3
1+5+2
2+3+3
2+4+2
3+2+3
1+2+3+2
1+2+2+3
If we only need to know IF a sum exists, there's no need for the result set - we just return true/false, and break out of the recursive algorithm completely when a sum has been found:
var requiredSum:int = 8;
var numberArray:Array = [1, 2, 3, 4, 5, 2, 3];
// Go through all possible subset sizes:
for (var subsetSize:int = 1; subsetSize <= numberArray.length; subsetSize++)
{
// We'll use the same array for all our attempts of this size:
var subset:Array = new Array(subsetSize);
if (findSum(numberArray, subset, 0, 0))
{
trace("Found our sum!");
// If we found our sum, no need to look for more sets:
break;
}
}
// numberArray : Our input set
// subset : The set we're currently filling
// setIndex : The position we're at in numberArray
// subsetIndex : The position we're at in the set we're filling
// RETURNS : True if the required sum was found, otherwise false.
function findSum(numberArray:Array, subset:Array, setIndex:int,
subsetIndex:int):Boolean
{
// Try every value from the input set starting from our current position,
// and insert the value at the current subset index:
for (var index:int = setIndex ; index < numberArray.length; index++)
{
subset[subsetIndex] = numberArray[index];
// Have we filled the subset?
if (subsetIndex == subset.length - 1)
{
var sum:int = 0;
for (var i:int = 0; i < subset.length; i++)
{
sum += subset[i];
}
// Return true if we found our sum, false if not:
return sum == requiredSum;
}
else
{
if (findSum(numberArray, subset, index + 1, subsetIndex + 1))
{
// If the "inner" findSum found a sum, we're done, so return
// - otherwise stay in the loop and keep looking:
return true;
}
}
}
// We found no subset with our required sum this time around:
return false;
}
ETA: How this works... As mentioned, it's the naive solution - in other words, we're simply checking every single permutation of numberArray, summing each permutation, and checking if it's the sum we want.
The most complicated part is making all the permutations. The way this code does it is through recursion - i.e., the findSum() function filling a slot then calling itself to fill the next one, until all slots are filled and it can check the sum. We'll use the numberArray [1, 5, 4, 2] as an example here:
Go through all subset sizes in a loop - i.e., start by making all [a], then all [a,b], [a,b,c], [a,b,c,d]... etc.
For each subset size:
Fill slot 1 of the subset...
... with each value of numberArray - [1, ?, ?], [5, ?, ?], [4, ?, ?]...
If all slots in subset have been filled, check if the sum matches and skip step 4.
(Recursively) call findSum to:
Fill slot 2 of the subset...
... with each remaining value of numberArray - [1, 5, ?], [1, 4, ?], [1, 2, ?]
If all slots in subset have been filled, check if the sum matches and skip step 4.
(Recursively) call findSum to:
Fill slot 3 of the subset
... with each remaining value of numberArray - [1, 5, 4], [1, 5, 2]
If all slots in subset have been filled, check if the sum matches and skip step 4.
(Recursively) call findSum (this goes on "forever", or until all slots are filled and we "skip step 4")
Go to 2.4.4.1. to try next value for slot 3.
Go to 2.4.1 to try next value for slot 2.
Go to 2.1 to try next value for slot 1.
This way, we go through every permutation of size 1, 2, 3, 4...
There's more optimization that could be done here, since the code never checks that it actually has enough values left in the input set to fill the remaining slots - i.e. it does some loops and calls to findSum() that are unneeded. This is only a matter of efficiency, however - the result is still correct.
I would do something like the following:
shuffle array
take random amount of numbers from the array
sum them up
if the sum is not the total sum you want, repeat
hm, not sure what you want to do at the end when a "conclusion" or "no conclusion" is reached, but you could generate a Power set from your set of numbers then for each subset add up all the numbers in it to see if you get your desired sum.
(This would be a 'brute force' approach and could be slow if you have many numbers.)
Possibly useful for how to create a Power set:
Calculating all of the subsets of a set of numbers

turn based movement on a tile grid, isolated tile(s)

This is written in as3 and FlashPunk.
My problem? I have a tank that can move 4 tiles in the 4 directions; north,south,east and west.
Image can be found at http://www.aroderick.com/isolatedTiles.jpg
I've started with a square grid (Lt gray on the image) and used an array to mark out a diamond pattern (in red on the image) from the square grid, which are all the possible movement choices given the tank can only move 4 spaces in the 4 directions.
The tiles with numbers on the image are the actual tiles you see in the game, the numbers are the "col-row" numbers.
Where is gets a little more complex is that I've also removed tiles from the diamond where there are obstacles (water,trees,mountains) which in turn increases the movement cost to get to tiles beyond the obstacle(s).
I should mention at this point this is based on the A* algorithm and uses A* for movement but these are the movement choices that need to be established before a destination tile is chosen.
My problem is isolated tiles that are beyond the move ability of the tank and are isolated on their own from the main movement area where tiles are joined and make a complete path for A* and the tank can be moved from one tile to the next.
Is there a simple, elegant way to deal(get rid of) with these rogue tiles?
I've tried a system of rules i.e.;
//same row going east
if(ob.row == co.row && ob.row == startNode.row && ob.col < co.col && ob.col > startNode.col && ob.c < co.c )
{
extraCost = co.c;
reason = 1;
break;
}
//same row going west
else if(ob.row == co.row && ob.row == startNode.row && ob.col > co.col && ob.col < startNode.col && ob.c < co.c )
{
extraCost = co.c;
reason = 2;
break;
}
Where "c" a a property representing the tile movement "cost" as the crow flies.
But these seem to create as many problems as they solve.
//reusable tile grid
public static function makeTileGrid(entityLoc:Point,moveGrid:Array,travelMax:int,tsize:int = 64):Array
{
//node list
var nodeLst:Array = [];
//counter
var tileCount:int = 0;
//for tile naming
var co_ordX:String = "";
var co_ordY:String = "";
if(moveNode == null) var moveNode:Object;
//subtract the tile range from the current location
//tile range times two because you can go forewards
//or backwards tRange spaces
for (var col:int = travelMax * 2; col >=0;col--)
{
//placeX is an x value so you must multiply both row and tRange by the tile width
var placeX:Number = entityLoc.x - (travelMax*64 - col*64);
//trace(placeX);
for(var row:int = travelMax * 2; row >=0;row--)
{
var placeY:Number = entityLoc.y - (travelMax*64 - row*64);
//trace(moveGrid[col]);
//use tile grid map array
if(moveGrid[tileCount] == 1)
{
//use coordinates for the name value e.g.
co_ordX = col.toString();
co_ordY = row.toString();
moveNode = {col:col,row:row,obst:false,node:co_ordX+"-"+co_ordY,nX:placeX,nY:placeY,ph:0,h:0,g:0,f:0,c:0};
nodeLst.push(moveNode);
}
tileCount ++;
}
}
return nodeLst;
}
My grid code. In case.
Thanks guys,
james-
I think you should use a limited (distance-wise) breadth-first A* search right over that grid, with obstacles playing as walls in the algorithm. This will generate a set of reachable nodes each with distance_left attribute available, in a way that your "isolated tiles" will not get listed. Your code seems to just get the diamond pattern out of starting position and passage matrix, without actual check of the path distance.
public static function getReachableTiles(startTile:Point,distance:int):Array {
var d:Dictionary=new Dictionary(); // will hold visited tiles point by point
var o:Object={d:distance,px:startTile.x,py:startTile.y}; // a simple object info
// add fields as necessary
var a:Array=new Array(); // output array
a.push(o);
d[startTile.y*256+startTile.x]=o; // the simplest hash. You have to ensure
// these never overlap for different pairs of [x,y], and the same hash
// function is used across this method
while (distance>0) {
for each (o in a) {
if (o.d!=distance) continue; // already parsed
var to:Object={d:distance-1,px:o.x-1,py:o.y};
if (Game.isPassable(to.x,to.y)&&!d[to.y*256+to.x]) {
// a new cell, and is valid for the tank to pass
// "isPassable" returns true if (x,y) corresponds to a passable and
// valid position. For example, (-1,2) might return false as it's off borders
d[to.y*256+to.x]=to; // add to dictionary - a parsed cell
a.push(to); // and to output
}
// doing the same for 3 other possible moves
to={d:distance-1,px:o.x,py:o.y-1};
if (Game.isPassable(to.x,to.y)&&!d[to.y*256+to.x]) {
d[to.y*256+to.x]=to;
a.push(to);
}
to={d:distance-1,px:o.x+1,py:o.y};
if (Game.isPassable(to.x,to.y)&&!d[to.y*256+to.x]) {
d[to.y*256+to.x]=to;
a.push(to);
}
to={d:distance-1,px:o.x,py:o.y+1};
if (Game.isPassable(to.x,to.y)&&!d[to.y*256+to.x]) {
d[to.y*256+to.x]=to;
a.push(to);
}
} // okay, entire array was parsed for new cells
distance--; // we've iterated A* once more, lessen the distance
}
// at this point, we've iterated A* "distance" times, return
return a;
}
You need to adjust this snippet for your variable structure, so that Game.isPassable() will be a valid call, and there would be the actual grid under your drawn map. Note, startTile is a grid-based set of coordinates, not pixel-based. Hope this helps.

AS3 - Not Retrieving Correct Item

In this section of code, I'm checking if a level in a game is unlocked or not. The Array "levels" contains 12 Movieclips and "levelsUnlocked" is the users highest level reached. So with this code, I'm checking if the level position is higher than the levelsUnlocked value, and if it is, it moves it to frame 2(which is the locked image), if is lower, it keeps it at frame 1.
My question is:
In the else statement, when I refer to levels[levels.length - i], it doesn't treat it as a Movieclip, but if I were to refer to it as levels[1] or levels[3], it does. Can someone tell my why this is happening?
function unlockLevels():void
{
for (var i:int = 0; i < levels.length; i++)
{
if (levels.length - i > levelsUnlocked)
{
levels[levels.length - i].gotoAndStop(2);
}
else
{
levels[levels.length - i].gotoAndStop(1);
}
}
}
Array length gives you the length of the array like this :
var a:Array = [0,1,2,3,4]
var length:int = a.length
trace(a)
//=> 5
in your first iteration in the loop your int is zero.
a[a.length - 0]
// a[5] == undefinded
You can use MovieClip(levels[levels.length-i]).gotoAndStop(2);
Edit:
Answer for your question:
You're trying to access the array elements from the end of the array while looping.
someArray[someArray.length-i] gives the "last - i" element of the array.

How do you loop through a circle of values in a 2d array?

Looping through a square section of a 2d array is easy but how do you loop through a circular section?
The way I've done this is to do a double for-loop like you would for looping through the 2d array normally. Inside this loop, however, do a check to see if the array element in question is within a circle of radius r using the distance formula.
For example, given a 10x10 array, and a chosen "center" of the array at (x,y):
for i from 0 to 9 {
for j from 0 to 9 {
a = i - x
b = j - y
if a*a + b*b <= r*r {
// Do something here
}
}
}
(Code is just pseudocode, not any particular language).
You can do much better than using trig functions (which are expensive) or using the equation of a circle, which will ultimately require the taking of an expensive square root.
There is a page devoted to the subject here:
http://www.cs.unc.edu/~mcmillan/comp136/Lecture7/circle.html
In essence the answer is that you want to figure out what the start and end points of the circle are on each row of your array. To do this compute an "offset" from the previous row, i.e. a "difference" from the starting point of the previous row (and similarly for the ending point).
This difference can be computed mathematically using the derivative. A refinement is to compute the difference between successive differences using the double derivative.
Anyhow, this abstract mathematical idea leads to the Midpoint Circle Algorithm, sometimes called Bresenehan's circle algorithm. See wikipedia for more detail on the algorithm itself:
http://en.wikipedia.org/wiki/Midpoint_circle_algorithm
I'm gussing you have something like this in mind
[ ][ ][x][0][ ][ ]
[ ][x][ ][ ][1][ ]
[9][ ][ ][ ][ ][2]
[8][ ][ ][ ][ ][3]
[ ][7][ ][ ][4][ ]
[ ][ ][6][5][ ][ ]
if that is so, you might have to use some basic trigonometry.
I would use the trig to advance the angle until you get the next value and add them to another array(or add the [i,j] coordinates to a new array), because the steps in angles wouldn't correspond to even steps.

How to implement three stacks using a single array

I came across this problem in an interview website. The problem asks for efficiently implement three stacks in a single array, such that no stack overflows until there is no space left in the entire array space.
For implementing 2 stacks in an array, it's pretty obvious: 1st stack grows from LEFT to RIGHT, and 2nd stack grows from RIGHT to LEFT; and when the stackTopIndex crosses, it signals an overflow.
Thanks in advance for your insightful answer.
You can implement three stacks with a linked list:
You need a pointer pointing to the next free element. Three more pointers return the last element of each stack (or null, if the stack is empty).
When a stack gets another element added, it has to use the first free element and set the free pointer to the next free element (or an overflow error will be raised). Its own pointer has to point to the new element, from there back to the next element in the stack.
When a stack gets an element removed it will hand it back into the list of free elements. The own pointer of the stack will be redirected to the next element in the stack.
A linked list can be implemented within an array.
How (space) efficent is this?
It is no problem to build a linked list by using two cells of an array for each list element (value + pointer). Depending on the specification you could even get pointer and value into one array element (e.g. the array is long, value and pointer are only int).
Compare this to the solution of kgiannakakis ... where you lose up to 50% (only in the worst case). But I think that my solution is a bit cleaner (and maybe more academic, which should be no disadvantage for an interview question ^^).
See Knuth, The Art of Computer Programming, Volume 1, Section 2.2.2. titled "Sequential allocation". Discusses allocating multiple queues/stacks in a single array, with algorithms dealing with overflows, etc.
We can use long bit array representing to which stack the i-th array cell belongs to.
We can take values by modulo 3 (00 - empty, 01 - A, 10 - B, 11 - C). It would take N/2 bits or N/4 bytes of additional memory for N sized array.
For example for 1024 long int elements (4096 bytes) it would take only 256 bytes or 6%.
This bit array map can be placed in the same array at the beginning or at the end, just shrinking the size of the given array by constant 6%!
First stack grows from left to right.
Second stack grows from right to left.
Third stack starts from the middle. Suppose odd sized array for simplicity. Then third stack grows like this:
* * * * * * * * * * *
5 3 1 2 4
First and second stacks are allowed to grow maximum at the half size of array. The third stack can grow to fill in the whole array at a maximum.
Worst case scenario is when one of the first two arrays grows at 50% of the array. Then there is a 50% waste of the array. To optimise the efficiency the third array must be selected to be the one that grows quicker than the other two.
This is an interesting conundrum, and I don't have a real answer but thinking slightly outside the box...
it could depend on what each element in the stack consists of. If it's three stacks of true/false flags, then you could use the first three bits of integer elements. Ie. bit 0 is the value for the first stack, bit 1 is the value for the second stack, bit 2 is the value for the third stack. Then each stack can grow independently until the whole array is full for that stack. This is even better as the other stacks can also continue to grow even when the first stack is full.
I know this is cheating a bit and doesn't really answer the question but it does work for a very specific case and no entries in the stack are wasted. I am watching with interest to see whether anyone can come up with a proper answer that works for more generic elements.
Split array in any 3 parts (no matter if you'll split it sequentially or interleaved). If one stack grows greater than 1/3 of array you start filling ends of rest two stacks from the end.
aaa bbb ccc
1 2 3
145 2 3
145 2 6 3
145 2 6 3 7
145 286 3 7
145 286 397
The worse case is when two stacks grows up to 1/3 boundary and then you have 30% of space waste.
Assuming that all array positions should be used to store values - I guess it depends on your definition of efficiency.
If you do the two stack solution, place the third stack somewhere in the middle, and track both its bottom and top, then most operations will continue to be efficient, at a penalty of an expensive Move operation (of the third stack towards wherever free space remains, moving to the half way point of free space) whenever a collision occurs.
It's certainly going to be quick to code and understand. What are our efficiency targets?
A rather silly but effective solution could be:
Store the first stack elements at i*3 positions: 0,3,6,...
Store the second stack elements at i*3+1 positions: 1,4,7...
And third stack elements at i*3+2 positions.
The problem with this solution is that the used memory will be always three times the size of the deepest stack and that you can overflow even when there are available positions at the array.
Make a HashMap with keys to the begin and end positions e.g. < "B1" , 0 >, <"E1" , n/3 >
for each Push(value) add a condition to check if position of Bx is previous to Ex or there is some other "By" in between. -- lets call it condition (2)
with above condition in mind,
if above (2) is true // if B1 and E1 are in order
{ if ( S1.Push()), then E1 ++ ;
else // condition of overflow ,
{ start pushing at end of E2 or E3 (whichever has a space) and update E1 to be E2-- or E3-- ; }
}
if above (2) is false
{ if ( S1.Push()), then E1 -- ;
else // condition of overflow ,
{ start pushing at end of E2 or E3 (whichever has a space) and update E1 to be E2-- or E3-- ; }
}
Assume you only has integer index. if it's treated using FILO (First In Last Out) and not referencing individual, and only using an array as data. Using it's 6 space as stack reference should help:
[head-1, last-1, head-2, last-2, head-3, last-3, data, data, ... ,data]
you can simply using 4 space, because head-1 = 0 and last-3 = array length. If using FIFO (First In First Out) you need to re-indexing.
nb: I’m working on improving my English.
first stack grows at 3n,
second stack grows at 3n+1,
third grows at 3n+2
for n={0...N}
Yet another approach (as additional to linked-list) is to use map of stacks. In that case you'll have to use additional log(3^n)/log(2) bits for building map of data distribution in your n-length array. Each of 3-value part of map says which stack is owns next element.
Ex. a.push(1); b.push(2); c.push(3); a.push(4); a.push(5); will give you image
aacba
54321
appropriate value of map is calculated while elements is pushed onto stack (with shifting contents of array)
map0 = any
map1 = map0*3 + 0
map2 = map1*3 + 1
map3 = map2*3 + 2
map4 = map3*3 + 0
map5 = map4*3 + 0 = any*3^5 + 45
and length of stacks 3,1,1
Once you'll want to do c.pop() you'll have to reorganize your elements by finding actual position of c.top() in original array through walking in cell-map (i.e. divide by 3 while mod by 3 isn't 2) and then shift all contents in array back to cover that hole. While walking through cell-map you'll have to store all position you have passed (mapX) and after passing that one which points to stack "c" you'll have to divide by 3 yet another time and multiply it by 3^(amount positions passed-1) and add mapX to get new value of cells-map.
Overhead for that fixed and depends on size of stack element (bits_per_value):
(log(3n)/log(2)) / (nlog(bits_per_value)/log(2)) = log(3n) / (nlog(bits_per_value)) = log(3) / log(bits_per_value)
So for bits_per_value = 32 it will be 31.7% space overhead and with growing bits_per_value it will decay (i.e. for 64 bits it will be 26.4%).
In this approach, any stack can grow as long as there is any free space in the array.
We sequentially allocate space to the stacks and we link new blocks to the previous block. This means any new element in a stack keeps a pointer to the previous top element of that particular stack.
int stackSize = 300;
int indexUsed = 0;
int[] stackPointer = {-1,-1,-1};
StackNode[] buffer = new StackNode[stackSize * 3];
void push(int stackNum, int value) {
int lastIndex = stackPointer[stackNum];
stackPointer[stackNum] = indexUsed;
indexUsed++;
buffer[stackPointer[stackNum]]=new StackNode(lastIndex,value);
}
int pop(int stackNum) {
int value = buffer[stackPointer[stackNum]].value;
int lastIndex = stackPointer[stackNum];
stackPointer[stackNum] = buffer[stackPointer[stackNum]].previous;
buffer[lastIndex] = null;
indexUsed--;
return value;
}
int peek(int stack) { return buffer[stackPointer[stack]].value; }
boolean isEmpty(int stackNum) { return stackPointer[stackNum] == -1; }
class StackNode {
public int previous;
public int value;
public StackNode(int p, int v){
value = v;
previous = p;
}
}
This code implements 3 stacks in single array. It takes care of empty spaces and fills the empty spaces in between the data.
#include <stdio.h>
struct stacknode {
int value;
int prev;
};
struct stacknode stacklist[50];
int top[3] = {-1, -1, -1};
int freelist[50];
int stackindex=0;
int freeindex=-1;
void push(int stackno, int value) {
int index;
if(freeindex >= 0) {
index = freelist[freeindex];
freeindex--;
} else {
index = stackindex;
stackindex++;
}
stacklist[index].value = value;
if(top[stackno-1] != -1) {
stacklist[index].prev = top[stackno-1];
} else {
stacklist[index].prev = -1;
}
top[stackno-1] = index;
printf("%d is pushed in stack %d at %d\n", value, stackno, index);
}
int pop(int stackno) {
int index, value;
if(top[stackno-1] == -1) {
printf("No elements in the stack %d\n", value, stackno);
return -1;
}
index = top[stackno-1];
freeindex++;
freelist[freeindex] = index;
value = stacklist[index].value;
top[stackno-1] = stacklist[index].prev;
printf("%d is popped put from stack %d at %d\n", value, stackno, index);
return value;
}
int main() {
push(1,1);
push(1,2);
push(3,3);
push(2,4);
pop(3);
pop(3);
push(3,3);
push(2,3);
}
Another solution in PYTHON, please let me know if that works as what you think.
class Stack(object):
def __init__(self):
self.stack = list()
self.first_length = 0
self.second_length = 0
self.third_length = 0
self.first_pointer = 0
self.second_pointer = 1
def push(self, stack_num, item):
if stack_num == 1:
self.first_pointer += 1
self.second_pointer += 1
self.first_length += 1
self.stack.insert(0, item)
elif stack_num == 2:
self.second_length += 1
self.second_pointer += 1
self.stack.insert(self.first_pointer, item)
elif stack_num == 3:
self.third_length += 1
self.stack.insert(self.second_pointer - 1, item)
else:
raise Exception('Push failed, stack number %d is not allowd' % stack_num)
def pop(self, stack_num):
if stack_num == 1:
if self.first_length == 0:
raise Exception('No more element in first stack')
self.first_pointer -= 1
self.first_length -= 1
self.second_pointer -= 1
return self.stack.pop(0)
elif stack_num == 2:
if self.second_length == 0:
raise Exception('No more element in second stack')
self.second_length -= 1
self.second_pointer -= 1
return self.stack.pop(self.first_pointer)
elif stack_num == 3:
if self.third_length == 0:
raise Exception('No more element in third stack')
self.third_length -= 1
return self.stack.pop(self.second_pointer - 1)
def peek(self, stack_num):
if stack_num == 1:
return self.stack[0]
elif stack_num == 2:
return self.stack[self.first_pointer]
elif stack_num == 3:
return self.stack[self.second_pointer]
else:
raise Exception('Peek failed, stack number %d is not allowd' % stack_num)
def size(self):
return len(self.items)
s = Stack()
# push item into stack 1
s.push(1, '1st_stack_1')
s.push(1, '2nd_stack_1')
s.push(1, '3rd_stack_1')
#
## push item into stack 2
s.push(2, 'first_stack_2')
s.push(2, 'second_stack_2')
s.push(2, 'third_stack_2')
#
## push item into stack 3
s.push(3, 'FIRST_stack_3')
s.push(3, 'SECOND_stack_3')
s.push(3, 'THIRD_stack_3')
#
print 'Before pop out: '
for i, elm in enumerate(s.stack):
print '\t\t%d)' % i, elm
#
s.pop(1)
s.pop(1)
#s.pop(1)
s.pop(2)
s.pop(2)
#s.pop(2)
#s.pop(3)
s.pop(3)
s.pop(3)
#s.pop(3)
#
print 'After pop out: '
#
for i, elm in enumerate(s.stack):
print '\t\t%d)' % i, elm
May be this can help you a bit...i wrote it by myself
:)
// by ashakiran bhatter
// compile: g++ -std=c++11 test.cpp
// run : ./a.out
// sample output as below
// adding: 1 2 3 4 5 6 7 8 9
// array contents: 9 8 7 6 5 4 3 2 1
// popping now...
// array contents: 8 7 6 5 4 3 2 1
#include <iostream>
#include <cstdint>
#define MAX_LEN 9
#define LOWER 0
#define UPPER 1
#define FULL -1
#define NOT_SET -1
class CStack
{
private:
int8_t array[MAX_LEN];
int8_t stack1_range[2];
int8_t stack2_range[2];
int8_t stack3_range[2];
int8_t stack1_size;
int8_t stack2_size;
int8_t stack3_size;
int8_t stack1_cursize;
int8_t stack2_cursize;
int8_t stack3_cursize;
int8_t stack1_curpos;
int8_t stack2_curpos;
int8_t stack3_curpos;
public:
CStack();
~CStack();
void push(int8_t data);
void pop();
void print();
};
CStack::CStack()
{
stack1_range[LOWER] = 0;
stack1_range[UPPER] = MAX_LEN/3 - 1;
stack2_range[LOWER] = MAX_LEN/3;
stack2_range[UPPER] = (2 * (MAX_LEN/3)) - 1;
stack3_range[LOWER] = 2 * (MAX_LEN/3);
stack3_range[UPPER] = MAX_LEN - 1;
stack1_size = stack1_range[UPPER] - stack1_range[LOWER];
stack2_size = stack2_range[UPPER] - stack2_range[LOWER];
stack3_size = stack3_range[UPPER] - stack3_range[LOWER];
stack1_cursize = stack1_size;
stack2_cursize = stack2_size;
stack3_cursize = stack3_size;
stack1_curpos = stack1_cursize;
stack2_curpos = stack2_cursize;
stack3_curpos = stack3_cursize;
}
CStack::~CStack()
{
}
void CStack::push(int8_t data)
{
if(stack3_cursize != FULL)
{
array[stack3_range[LOWER] + stack3_curpos--] = data;
stack3_cursize--;
} else if(stack2_cursize != FULL) {
array[stack2_range[LOWER] + stack2_curpos--] = data;
stack2_cursize--;
} else if(stack1_cursize != FULL) {
array[stack1_range[LOWER] + stack1_curpos--] = data;
stack1_cursize--;
} else {
std::cout<<"\tstack is full...!"<<std::endl;
}
}
void CStack::pop()
{
std::cout<<"popping now..."<<std::endl;
if(stack1_cursize < stack1_size)
{
array[stack1_range[LOWER] + ++stack1_curpos] = 0;
stack1_cursize++;
} else if(stack2_cursize < stack2_size) {
array[stack2_range[LOWER] + ++stack2_curpos] = 0;
stack2_cursize++;
} else if(stack3_cursize < stack3_size) {
array[stack3_range[LOWER] + ++stack3_curpos] = 0;
stack3_cursize++;
} else {
std::cout<<"\tstack is empty...!"<<std::endl;
}
}
void CStack::print()
{
std::cout<<"array contents: ";
for(int8_t i = stack1_range[LOWER] + stack1_curpos + 1; i <= stack1_range[UPPER]; i++)
std::cout<<" "<<static_cast<int>(array[i]);
for(int8_t i = stack2_range[LOWER] + stack2_curpos + 1; i <= stack2_range[UPPER]; i++)
std::cout<<" "<<static_cast<int>(array[i]);
for(int8_t i = stack3_range[LOWER] + stack3_curpos + 1; i <= stack3_range[UPPER]; i++)
std::cout<<" "<<static_cast<int>(array[i]);
std::cout<<"\n";
}
int main()
{
CStack stack;
std::cout<<"adding: ";
for(uint8_t i = 1; i < 10; i++)
{
std::cout<<" "<<static_cast<int>(i);
stack.push(i);
}
std::cout<<"\n";
stack.print();
stack.pop();
stack.print();
return 0;
}
Perhaps you can implement N number of stacks or queues in the single array. My defination of using single array is that we are using single array to store all the data of all the stacks and queues in the single array, anyhow we can use other N array to keep track of indices of all elements of particular stack or queue.
solution :
store data sequentially to in the array during the time of insertion in any of the stack or queue. and store it's respective index to the index keeping array of that particular stack or queue.
for eg : you have 3 stacks (s1, s2, s3) and you want to implement this using a single array (dataArray[]). Hence we will make 3 other arrays (a1[], a2[], a3[]) for s1, s2 and s3 respectively which will keep track of all of their elements in dataArray[] by saving their respective index.
insert(s1, 10) at dataArray[0] a1[0] = 0;
insert(s2, 20) at dataArray[1] a2[0] = 1;
insert(s3, 30) at dataArray[2] a3[0] = 2;
insert(s1, 40) at dataArray[3] a1[1] = 3;
insert(s3, 50) at dataArray[4] a3[1] = 4;
insert(s3, 60) at dataArray[5] a3[2] = 5;
insert(s2, 30) at dataArray[6] a2[1] = 6;
and so on ...
now we will perform operation in dataArray[] by using a1, a2 and a3 for respective stacks and queues.
to pop an element from s1
return a1[0]
shift all elements to left
do similar approach for other operations too and you can implement any number of stacks and queues in the single array.