What I’m doing:25x25x25 “playing area”2 3D arrays. New gen and Old gen.Changes to old gen are saved to new gen.New gen is displayed.New gen becomes old gen.And then it changes are made to old gen and saved to new gen...

This will go till I tell it to stop.I plan for it to be 2-4 generations a second, so you can see it progress.I’m using a raspberry pi 2, so will memory and stuff really be a problem, will I need to change my code to compensate for it?

My meta for future reference

Spoiler:

cemper93 wrote:Your meta appears to be "just writes whatever is on his mind and doesn't remember what happened more than five hours ago"

If I generate a 250x250x250 list-of-lists in the python shell, it starts consuming around 500MB. (which would imply some 32 bytes per element) So don't expect a playing area that's 1000 times as small to have a problematic impact on memory.

For that size, I don't see any problem with any method, though the base overheads of the non-array/hybrid versions won't help. 25³ array is what I'd use, for that. The rest is just overkill, and awkward with it.

(Question: Have you thought about edge conditions? Are you doing wrap-around (finite but boundless, presumably toroidal) or are you adjusting the thresholds (making it, say a given percentage of the viable neighbours (26, 17, 11 or 7, assuming 1-deep influence including all diagonals), rather than absolute numbers assuming always 26 neighbours and just fudging the edge?)

It'd be interesting to see if you could use something like this 8x8x8 cube, for output. Now you've got me wondering how to mutiplex that. At some point, it'd involve three 25-pin connectors, but possibly doable with analogue voltage levels handling activation levels (e.g. 5 voltage levels, 0 to Vmax in ¼Vmax intervals, on six outputs that key a 6-digit pental discriminator, two pental digits per dimension).

(15,625 LEDs, though, and likely at least 625 transistors betwixt the first and second input 25s, whatever else you have to use to swing them high and the third 25 low on the output pin. And that's with raster-scanning illumination. Can you power an LED for 1/15625th of the time and still make it visibly lit? Perhaps you need to individually latch them on, as well, which is at least 15,625 transistors with loopbacks onto the input and the common base raised high on them all to reset/whatever.)

Yeah, I’ve made a set of rules for the 3D thing, we’ll se how it works.Ones that share a vertex have a weight of 0, and ones that share an edge have a weight of .5, and ones that share a face have a weight of 1...

I might make it boundless if I want to, but then I’d use Pi3D instead of Minecraft as the graphics engine...We’ll see how it goes, I think I’ll be able to work more on it tomorrow.

My meta for future reference

Spoiler:

cemper93 wrote:Your meta appears to be "just writes whatever is on his mind and doesn't remember what happened more than five hours ago"

Peaceful Whale wrote:I might make it boundless if I want to, but then I’d use Pi3D instead of Minecraft as the graphics engine...

Interesting. (I fell out of love with my Minecraft Pi version when I realised it was a limited size "god mode" builder, had no mobs and I couldn't even do all the things I used to do with the PC version1 that I had hogged on someone else's machine. But as a quick-and-dirty displayer of external info..? Well, that's what Pi developmemt is supposed to encourage, and only look like a game. So why not?)

But what I meant by finite-but-boundless is just how it treats the edges. For X/Y/Z of 0..24, the 'upper' neighbour of 24 is 0, and the 'lower' neighbour of 0 vice-versa. That'd be in your raw code, doesn't matter how it then gets rendered.

Pseudocode (maybe some Perlish elements, though I was aiming for generic C-family so ignore them), made flexible:

1 Beta? Pre-MS. It had the Endermen, but it was before The End and all that other magic stuff. Apparently you can work with raw magma these days having quaffed a potion to be immune. No challenge at all. One of the reasons I'm not tempted to

(I'd just like to point out that, first of all, my battery is warning that it's very low, thus the low quality/consistency of my pseudocode once I got in a rush. But also that I mentioned Minecraft as an off-hand example of storage requirements, not knowing it was being used as graphical front-end by our frien, here. No link there, in my intent. And I'll consult your link, Tub, when I've got double digit battery percentage again (or, likely, when I'm back on a non-mobile connection, whilst chargung is going on as well). Don't mind me. Just thinking aloud! )

Best guess, a, b, c cross loose type-bounds like a ring-species. Say maybe "0.0" != "0" as text but 0.0 == 0 as values, yet text of a value can get so (without checking) doing 0=="0.0" gives truth, 0="0" gives truth but "0.0"!="0". (Doesn't happen in Perl, they'd all be true-on-equal, even explicit "0"=="0.0". Its strength and weakness1.)

Next guess, the opposite direction. Something funny like NAN (does that exist in javascript?) that you might need to produce by doing a compound item in the assignment. There are things that do not equal themselves, but can be made to equal (sometimes via contrived means) other values.

Is it javascript-specific? Java-family? Possible further out amongst the wider C-like-ish clan, at least those not strongly typed?

1 -edit: To expand on that, using eq/ne instead of ==/!= does textual comparisons for textual accuracy, and a bit of leaway for defined-as-value. If we assign ($a,$b,$c)=(0,"0","0.0") then ($a eq $b) but ($a ne $c) as well as ($b ne $c). But this is neither the answer we need, actually surprising when you pomder it nor is it within the bounds of the puzzle. Just FYI.

Aha. Further than "different types of numbers" but closer than "into the really esoteric stuff".

I occasionally use truth or falsity tests directly as 1 or 0 in mathematical constructs, so I might have guessed that. But, perlwise, ("0" == "") almost certainly complains (if warnings are used) about the "" being non-numeric, and a literal [0] or [1] is an Array reference treated in this regard as a specific non-blank string that wouldn't match anything else (even a neighbouring matching [0] or [1] not explicitly copied from the original).

But nice to know!

(As is the "use = instead of == in a test… Hey! What went terribly wrong?!?" common error. )

Soupspoon wrote:But what I meant by finite-but-boundless is just how it treats the edges. For X/Y/Z of 0..24, the 'upper' neighbour of 24 is 0, and the 'lower' neighbour of 0 vice-versa. That'd be in your raw code, doesn't matter how it then gets rendered.

Pseudocode (maybe some Perlish elements, though I was aiming for generic C-family so ignore them), made flexible:

It's one way of doing one thing. It might not be the thing you want to do, and it might not be the exact way you'd want to do the thing. You could perhaps make the call to Assess(x, y, z) check-and-correct the out of range (with a TrueVal() function) before passing to RealAssess(validX, validY, validZ) so as to not bother your function. Also, instead of dX going from -1 to 1, and offsetting the 'core' X, you could do a lookX that runs from (X-1) to (X+1) and do the 'is it corner, edge, face or core?' by direct check of whether lookX==coreX, lookY==coreY, lookZ==coreZ, but that's going to be an eight-leafed triple-branching check such as (I think!):

Thus I quite like the dX,dY,dZ way of doing this kind of thing, as a way of extracting and combining the unit disolacements to determine the Manhattan Distance from the origin to the check-coords.

And then I made an obvious error. Please note this as you transcribe it. I couldn't recall what the signum function was, in Python. (Look for signum(), sgn() or sign() functions/methods. It's a neat monolithic way of getting +1, 0 or -1), and what I then meant to write was that signum(x)*x would give 1 for x=±1, zero for zero, to add to the similar results for y and z, giving 0 total at the core cell, 1in total at a face-on-face neighbour (six of them), 2 for edge-centres (12 of them) and 3 for the corners (8 of those, bringing the total to the 27 you'd expect).

(If you do this correctly, you can also plan to use this for (beyond your scope, but worth considering) next-to-neighbour assessment (dX=-2..+2) if you wanted, as signum(-2)*(-2)=>+2 (two away from origin, regardless of direction, and the reason I was doing signum*original at all, rather than just using the signum) and classify weightings in a useful manner for all 125 cubes in a up-to-2-distance offsetting on the origin cubes.)

And then I realised that I'd completely overcomplicated things. Look for an abs() function, 'absolute'. That makes -n straight into n. Doesn't need sgn(). Doh! But now you see where I was going, in a rush as the battery rapidly depleted.

In fact, given you're only going ±1 from the origin, just do a square. dX*dX (etc). ±1 => +1 and 0 => 0, without fuss. It's only values beyond 1 (either sign) or non-zero fractions of 1 that this might be wrong for your purposes, but those don't apply to you!

Would I just use this?

By the Gods, absolutely not! I did my best to make it easier to translate my pseudocode to the Python than if I'd written it in valid Pascal, Ada, Perl, COBOL, etc, which I would have had more confidence in. You need to run through the lines and assume they are all comments, then rewrite tye bits in your own favourite style of actual Python. (Noting the above.)

Is there a different way?

Always! I'm not even saying that the above way is the best way. It's one I'd favour, off the bat, but testing and development might force a rewrite because of an issue. Whether that be merely style, flexibility or because I've erred. Like I might have done, had I been doing the live programming myself!

Could I make it so it’s always the smallest array possible? Or just 3 bigger?

I'm not entirely sure what you mean by this. In a wrap-around 'world', the array is the size it is (change it, and you shoehorn cells in there, between neighbours to make them no longer neighbours).If you're doing "move the edges out before they become significant" so that you have effectively infnite space, only not frommthe start, you need (at the simplest) to tack on a test each time a cell is set as 'live' to make sure that once you get to xmax (or within a decent range of it) you resize/reallocate the array to give you a new higher xmax to move into. Same with y and z, and ditto with x/y/zmins at the lower limit.

Beware 'explosions'. Even if you also regularly track deaths (and no births) of cells to raise your min values and lower your max ones, if you've got a pattern that sends elements out in all directions (even if it leaves the centre and most of the other directions devoid of live cells) the array will just resize larger and larger and larger. By doing a 'smart review' you might be able to identify a fragment travelling right+forward+up (with no 'debris' left behind) and a fragment travelling left+backwards+down (ditto) that can each be self-contained in their own very small box. But that's part of the "dictionary store of references to arrays" thing that I'm not sure I explained properly. (Sort of mentioned along the way in Tub's link, an interesting read indeed, but that won't help you.)

It's why I thought you might prefer the wrap-around (static) array. Simpler, though functionally different from the version without wrap-around, just ever expanding shoulder-room whilstsoever required.

(As long as you realise that we're talking about specific and possibly theoretical patterns, discovered or designed to do this and that most configurations of even a sympathetic and enabling 3DConwayGoL setup aren't going to be quite so neat...)

The simplest, most naive way would be to have a set of data-objects (themselves in a basic array, a linked list, whatever floats your boat) with first of all a fairly tight and centred-on-your-Glider standard 3D array, and secondly three offsets for XYZ. As the glider-thing moves within its 'cage', your simulation checks where within the array this next step of the glider is now placed, and if it finds that it is off-centre, it shuffles it back to the centre (by whichever unit x, y and z displacements it deems necessary) and then it adds the removed position onto the cage-array's associated XYZ (which presumably is tracking the absolute XYZ of the centre-of-cage cell, which may be 0,0,0 as far as the cage itself is concerned), so that if it is deemed that a set of cells move 1 cell in the X direction and -1 in the Z direction, within the cage you shuffled everything to x-- and z++ while using X++ and Z-- on the cage's associated information.

You need to get to know how to handle more complex data records. In the old days, you'd do something like "define record Cage= field:array(5,5), X:int, Y:int, Z:int" or somesuch. In strict object-orientated definitions, you'd define a class of Cage with a field array and X, Y, Z elements as public.

A quick-and-dirty way for a type-loose language is just to make an array of four elements, the first element being further the array of array of array that is the cage/field/whatever construct (the thing you actually came here to learn about, if you recall ) and the second, third, fourth elements can store your XYZ values.

If you do the Q&D way, it's possible to just directly refer to Cage[0][3][-1][0] for the element 3,-1,0 of the 'travelling cage' and Cage[2] would be the Y displacement, but writing that acurately all over the place is liable to all kinds of errors, so create reading and writing subs that are the only non-test accessors of the very buried data, check they do it right (with those test/debug methods, removed/commented-out once validated to sufficient satisfaction) and then only use these. e.g. something you could call with CheckCell(CageRecord, X, Y, Z) that looks at the Cage for the cage-stored displacements, adjusts the X, Y, Z with them to get the to 0,0,0-centred equivalnt for the cage and then examines the Cage's array³ item for the actual cell.

You'd also need a 'collision check' between cages (to see if they were close enough to interact), a method of merging cages (a new cage with an array-thing sized to hold the two collisioning cages, and its own XYZ offsets being worked out afresh from the actual absolute XYZ that this new cage's centre must have) and of course functions to identify splitable cages (where there's a clear planar gap, between parts of an existing single cage) and then split them (subsetting into smaller arrays, recalculating each of the associated XYZs as a reverse of the merge function) because that is how you get multiple independent cages out of your initial 25x25x25 setup, or whatever size it has managed to bloat/shrink to through basic border-management of the original.

This is a complicated thing to do. Even to think of. If I could quickly scribble some diagrams to illustrate what I mean, I'm sure it would be far better than all these words I'm using.

And also: Maybe something to think about later, not right now. Do your original project, first. Get the hang of the array access, for a start.

And also also: I still do not claim that it's the absolute best way of doing this complicated thing, and it might not even be in the top ten, but I'm laying it out there as theory, based upon experiences in unrelated but relatable coding problems I have worked on. (It is from those, mostly, that my record/class/irregular-deep-layered-array alternate methods arise. Whatever you do, add copious comments to explain things when you make a seemingly arbitrary decisision on how you pack your data. It helps people with less than a full familiarity of the project, which includes "you, diving back in after a few weeks doing something else, and now wondering what everything does".)

Note that, as long as you don't have more than a couple of tens of thousands of live cells, you could use the dictionary/set approach suggested earlier: put the coordinates of live cells in a tuple (x,y,z) and add it to the set and remove it if the cell dies. Since adding, removing and testing existence takes about constant time, it shouldn't slow down too much when you get lots of living cells. And the overhead of a set isn't all that extreme.

The greatest advantage is that you can write short, readable code for an infinite grid. It may waste memory compared to an algorithm that checks for and optimizes patterns in the simulation, but it's much easier to .pop() random cells once you hit 100MB of memory than to write such advanced algorithms.

Oops, I beautified it manually because I feared some conversions in JSON.stringify would be lossy (like undefined turning into null), but Array(0) is of course an empty array, not an array containing a zero.

Tub wrote:There's one more case that's even sneakier. Worry no more about transitivity. You can solve this:

Points to hotaru for this one. When comparing an object to a native value, javascript will first try to "unbox" the object, which is why new Boolean(false) == false and 42 == [42]. Unboxing may also include calling valueOf() or toString().

But you don't need unboxing. You don't need 'a' to be an object. You don't even need type coersion. See?

Yeah, that's the idea. a is no longer a local variable, but a a global variable, i.e. property of the global object. Properties can have getters!

Getting the global object in a compatible way is tricky, but possible: call a function without a context, then it'll use the global object as 'this'. Except in strict mode, so make sure the function doesn't run in strict mode:

For the rotation function, I’m doing a bunch of 3D sine, cosine, and tangent stuff... that way you can rotate the image with the mouse...It’s a good thing we just learned about stuff like this in geometry...It’s kinda pretty, but I made an error in the code... so now it only spins one way... I figured out what’s wrong and now it works nicely.

My meta for future reference

Spoiler:

cemper93 wrote:Your meta appears to be "just writes whatever is on his mind and doesn't remember what happened more than five hours ago"

So: I’ve got most of my stuff wrote out, and I’m testing little batches... however when I do I get this error:“List Index out of Range”My 3D Array is formed fine, it is filled fine, however when it is scanned, it through that error. This section of code is where it is happening...

Replace *tab* with actual tabs of course... the error is from the if cubes_old == 1 thug. No matter what cell I tell it to check it throws that error. I feel like this is a really nooby mistake, and would appreciate help.

This isn’t the full code, I trimmed out the unnecessary stuff, however if I run this code it still gives me the error...

My meta for future reference

Spoiler:

cemper93 wrote:Your meta appears to be "just writes whatever is on his mind and doesn't remember what happened more than five hours ago"

The usual error *I* make (if I translate code between languages and forget the differences between the two) is to try to access the nth element of a [0..n-1] array, or the 0th item of a [1..n] array. Obviously Python is so loosely typed that it works for either as long as you nudge the array to cover where you're expecting.

However, I note in reading your sample (I assume the "*tab*" is your indents... Code-tag usually preserves spaces, which ought to do much the same, I think, as a tabbing indent) that you have max_z in both the +10 (maxes) and -10 (mins) assignment lines. Gets set as +10, then reset as -10 whilst min_z doesn't get set at all.

Could it (in the absence of a previously defined min_z) end up trying to do a 'z' in range (undefined|zero to -10), which a [..][..][3] reference is obviously outside, pretty much however Python parses the erroneous ranging.

That's if the error is in your original code, that is, and not actually your transcription over to here. Which is also easily possible but then leaves the original error as almost unaswerable at present.

(Adding the appropriate format of printing "Now setting Foo to value "+Foo+"!" to strategic points will reveal this kind of thing, as suggested. I tend to maintain a 'portable debugging-line' that I move and copy around the shop, just as much as needed. Sometimes I go so far as to set a global DEBUG_LEVEL variable and then something like if DEBUG_LEVEL>5 then print "FuncAddItAll: Currently adding "+new_figure+" to "+running_total; or somesuch, which I can switch on and off, according to the detail I need. Then, at or near the end of testing a section, search for all lines with DEBUG_LEVEL mentioned and delete some or all of them when I'm cleaning up afterwards. But I'd be just as happy to comment the more basic version of the lines out, once I think I'm happy enough with what I'm being told, but it then lets me uncomment them again if I get a pang of renewed uncertainty after a later run-time error or other surprise.)

for x in range(min_x, max_x): for y in range(min_y, max_y): for z in range(min_z, max_z): cubes_old[x][y][z] = 1

gives me an error, because cubes_old[x] isn't assigned to anything yet. A 3-dimensional array is an array of arrays of arrays, but you have to make all those arrays yourself; that's where this code is useful.

Finally, array indexes can't be negative; Python interprets a[-1] as meaning the last element of a, a[-2] means the second-to-last element, etc. Therefore, even if your code did work, you'd likely run into bugs where multiple numbers end up referring to the same array element. Either start at 0, or use dictionaries instead of arrays. (If you use dictionaries, you can use tuples as keys, i.e. cubes_old[x,y,z], and then you won't have to worry about the problem I mentioned in the previous paragraph.)

Ok, thank you...I did originally have each variable declare separately, (min_x =10... etc) but when I put it here, I made it different...The no negative numbers in arrays is going to be annoying... I was trying to make it expandable, but then I’d have to move it from only 3 sides of the cube array... ad then have to move all of the cubes so they’re centered...?I will fix stuff, thanks for your help!

My meta for future reference

Spoiler:

cemper93 wrote:Your meta appears to be "just writes whatever is on his mind and doesn't remember what happened more than five hours ago"

As noted, it does look like you've re-typed your code here in the forum, rather than copy-pasting the code you're actually trying to run, and I think in the process of re-typing it there's been some typos introduced. So it's hard to tell exactly what errors are contributing to the bug you're experiencing, and what are just transcription errors.

So there are any number of things your problem might be, but we'd have a much better shot at decyphering the error if you copy-pasted the actual code you're trying to run.

With that said, I'm going to ignore the more typographical things (assigning to the wrong variable, weird broken assignment syntax, strange indenting) and look at the sort of things that are more likely to be the actual problem.

First: list indexes start at zero, and count up. If you want to represent starting from -10, you'll need to do something like add 10 to the indexes whenever you look them up in the list. Same as the C that, as I understand it, you're familiar with.

Second: The set-item statement, "thing[index] = stuff" only works if that slot in the list exists. So, for example, if you have a list that's five entries long, you can assign to thing[0] through thing[4], but if you try to assign to thing[5] you will get an IndexError. So, in your code, you make an empty list, which contains zero items... so trying to assign to any index will give you an IndexError. There are methods to change the size of lists, append items, etc, but I'm not going to go into much detail about them here since you probably don't actually want that, as I understand it you're more after one big statically-sized array. But this is why a lot of the examples people posted before on how to make a big 3D array did things that would construct a big list-of-lists that already was filled up, even if it's just filled up with meaningless values like 0 or None.

[edit] I see I've been beaten to the punch on most of these points. But still.

Lately I have been working on force directed graph layouts. Currently trying to port (is that the right word here?) my naive implementation to numpy because with the 400 node, 900 link test graph I'm using it takes several seconds per iteration.

A further error - array indexes are 0 or larger. If you use negative indexes, in Python it doesn't refer to another item to the "left" of the 0th item, it instead starts counting from the *end* - -1 is the last element, -2 is the second from last, etc.

If you need arbitrary coordinates, you want to use one of the models people have talked about previously, where you're using a dict, rather than an array.

How would I add dictionary references?I don’t understand how I would use a dictionary...They’re like a dress books right? Name gives you a value... how Orel I add names and values? Would it make more sense?

My meta for future reference

Spoiler:

cemper93 wrote:Your meta appears to be "just writes whatever is on his mind and doesn't remember what happened more than five hours ago"