Scalable Conway's Game of Life

As my first foray into playing with Snap! I thought I'd try my hand at building the game I've probably coded in every language I've ever programmed in including apple basic on the old apple IIe (though this version runs SIGNIFICANTLY faster :rofl:) Conway's Game of Life

Hopefully this is the right project link

I wanted to touch as much of the snap general language feature in this endeavor as possible.

Going to shout out @s732939 for his version ( though it does seem to have a bug because none of the normal oscillators, spaceships , etc work properly) watching it run really helped me to understand how Snap! runs.

Playing:

  • Game can be sized anywhere from a 1 x 1 grid ( though you really want it at least 10 x 10 for any thing significant to happen) up to any size grid (after 30 x 30 it REALLY slows down)
  • Cell can be any size from 1 unit square up to whatever size the stage can support
    • Cell costumes are built automatically during game setup
  • Game will be drawn centered on whatever current point the pen starts at (the project currently sets 0,0 for simplicity)
  • Key based controls
    • p / P / Space Bar - pause
    • r / R - Randomize cells
    • q / Q - quit game

I'm sure as with everything I code I'll come back to it in 6 months to a year and be appalled at the monster that created this abomination. But as my knowledge of Snap! is right now I'm quite happy with it.

I really like your block

.

Mmm … why not generalize it?

Now it can be used for any save / set / restore sequence … but, wait …

What if the act of restoring the old value were built in?

Now we don’t even have to see the old value, or restore things after the action(s) with the new (temporary) value.

Quite often multiple parameters need to be saved, changed and restored. Now the handling of multiple parameters may be nested, like:

Conways Game of Life script pic 5

One more step
(BTW “Let” is an block of my own, combining “Variables” and “Set”; using an upvar, of course)


with:
Conways Game of Life script pic 7

Proof of concept

Wow that is some awesome stuff. When I've got some quiet time going to look through your examples

This was my literal first week, first project with Snap! got my eldest (he's 12 ) a binder looking book that uses Snap! to teach programming concepts through a fun little comic strip leading up to him building a simple video game all while getting the core concepts down pat. I've been programming semi and fully professionally for over 30 years now but I've never worked with something like that. Sometimes its hard for me to wrap my head around how I would implement something I'm used to like a switch / case block within the limits of the language.

I also put a limit on this little project to ONLY use the native Snap! i.e. no external libraries, no ECMAScript support (heck that alone I'm proficient enough in ECMAScript most of these hurdles would have been extremely trivial to code out) because my son will only be dealing with pure Snap! in this book he got for Christmas (He's already excited to go through it and learn some stuff) and I want to have at least a cursory understanding of how Snap! executes, how things are put together, etc. so when he has questions I can give him a little bit of guidance. Not expecting anything close to a game like this from him out the gate but i'm excited he's showing an interest in coding even if he never actually uses it for a job or career its a skill i think every child should learn at least the basics. The real life benefits of knowing basic programming even in problem solving outside those solvable with a keyboard are huge in my opinion

Thanks for the detailed break down by the way I'm looking forward to breaking it apart to understand it

Nice project, but you forgot to add the project notes.

Wait a minute...

I tried making a blinker pattern and it doesn't work!

A blinker is a 3-length line. I drew
:white_large_square: :white_large_square: :white_large_square:
:black_large_square: :black_large_square: :black_large_square:
:white_large_square: :white_large_square: :white_large_square:

and expect the next frame to be

:white_large_square: :black_large_square: :white_large_square:
:white_large_square: :black_large_square: :white_large_square:
:white_large_square: :black_large_square: :white_large_square:
but it disappeared instead.

I have more information on Conway's Game of Life and feel free to use it! I hope this will fix the project as I said above.

Info

Each rule has a string B#/S#, where each # means any number from 0 to 8 appear once or not appear at all.

012345678, but not 9 because we're talking about the 8 neighbouring cells about a square.

Conway's Game of Life uses the string B3/S23, where B3 meant that if an empty cell has 3 filled cells around it, it will appear the next frame. B23 meant that if a filled cell has 2 or 3 filled cells around it, it will stay.

So, say this blinker pattern
:white_large_square: :white_large_square: :white_large_square:
:black_large_square: :black_large_square: :black_large_square:
:white_large_square: :white_large_square: :white_large_square:
(white are empty and black are alive)

The top and bottom (not any corners) wlll appear the next frame because they both have 3 neighbouring cells (including diagonal neighbours). But the left and right will disappear because they only have 1 neighbours when they should have 2 or 3 to stay alive. The middle will persist because they have enough 2 neighbours. Steps are did all at once, not one after another.

Hopefully this helps.

EDIT: I read the code and I'm sure the rules are correct, but I think it's not working due to the steps doing one after another. I think you can fix it by implementing the current and future frame variables.

I even made some known patterns, such as the 2-by-2 square, a glider (too lazy to tell what it is, but it moves diagonally).

Great work and effort, please don't delete the project...

EDIT 2: I read the topic post and of course you already knew that none of the usual patterns work properly!

Sorry for abusing the topic you started - I was triggered by a block in your code, developed a train of thought, and got a bit carried away by it, perhaps. Even so, I’m definitely going to use the end result for my own projects.

You certainly made a grand entrance!

BTW you may be interested in a Game of Life on an infinite canvas, which I made as a “remix” (i.e. a further development) of an original project by @joecooldoo. Read the Notes (file menu) for background and instructions.

When you open any project, you can save (a copy of) it to either the Snap! cloud or your own system.

Nice.

It took me quite a while to work out why you were doing an extra TOGGLE PAUSE if there's an odd number of clones! I think a more usual solution to that problem would be

so only the parent of all the clones does the toggle. (You can do even fancier things, arranging for the parent and the clones to have different local procedures, but that's overkill. This way is self-documenting.)

I'm a little confused by this:


You call it outside the FOREVER loop, so how does each cell respond to changes in its neighbors? (Maybe I'd understand this better if it weren't 3am.)

That WAIT 0.01 SECS in LIVE OR DIE BY is kind of a red flag to me. I admit that on occasion I've had to rely on that, but really it should be possible for the code to keep track of the precise condition it's really waiting for. In this case, it means "wait for my 900 siblings to update," so I can imagine that 1/100 sec might not always be long enough! I think the standard thing to do is keep a count of how many clones have updated, and wait until that's the total number of clones.

By the way, have you tried using turbo mode instead of WARP? The only computation the warped code is competing with is the same code, also warped, in siblings, so I think the real benefit of the WARP is to lock out display updating, and turbo mode will do that without serializing the computation.

Also in LIVE OR DIE, I'd be inclined to move the WAIT UNTIL at the end to after the call to LIVE OR DIE in the toplevel WHEN I START script. The way you have it, I couldn't understand the point of a WAIT immediately followed by the end of the procedure.

I'm pretty sure I've seen cases in which the result of a starting pattern depends on whether it's run in single-step mode (with PAUSE true) or not, but I can't immediately reproduce it.

I don't think they were referring to accidental deletions. It's appallingly common for students, sometimes even quite expert students, to react to a bug by deleting the project on purpose. I blame school, which teaches you that you have to get everything right the first time. Whereas the right attitude towards bugs is that a hundred-line program with a bug in it is 99% correct. I tell students that, but they're not always convinced.

I don't know why but playing it on the player does not work but if you edit it and run it it works fine.

I tested all the patterns, spaceships etc

First time with snap so maybe there's something i'm missing between how it runs in the IDE and when someone just hits run on the shared view

Oh don't worry I've been teaching others how to code almost as long as I've been coding ( no joke taught an 85 year old lady in a nursing home how to program in QBasic as part of a volunteer program i was part of in high school going out and spending time at a local nursing home, she was so excited to just be doing anything with anyone it was one of the defining moment where i knew anyone with the right motivation can learn the basics of code. Since then i've taught everyone from fresh faced helpdesk workers with zero experience programming who went on to SysAdmin positions with those skills, to fellow engineers that understood enough to copy and paste together enough from the internet to get a job done into people using consistent coding standards, documentation, etc to be able to share their code with others and collaborate on other people's work.)

Watching someone else grasp a programming concept is almost as enjoyable as the thrill I get when a particularly complex problem finally reveals the simple elegant solution I've been missing.

I say all that to say probably THE core tenant I've developed over the years that I try to drill into anyone I teach to code is its ALL about iteration. I grew up building houses with my father (Yes I can and have done things like tile a bathroom floor, or rebuild an entire bathroom from replacing rotten floor joists to installing a new tub) and that's one of the biggest lesson I learned from him. NOTHING worth doing is right the first time, not even close, not even the second, third or fifth, maybe if you've honed your skills by the 10th you've got something functional.

Its the tiny little adjustments you make as you learn things, test things that really make things great. Some may just fix or improve one small are, OTHERS can inspire sweeping changes improving areas you didn't initially think about, and they all coalesce into a broader mental tool set for future use. That mindset of iterating over a problem coupled with the willingness to literally fall flat on your face and fail and get back up and assess, adjust and repeat I firmly believe are the reason I get paid the (still insane to me) amount of money I get paid to fail 90% of the time I spend working to produce things others actually want to and can learn.

I'm always learning myself (hence why i'm here) and I don't exempt myself from the mindset I teach others.

I really was / am excited to digest what you put out there.

I was actually talking about the other guys game I had found to run so I could see how Snap! operated I knew my filtering logic was solid (wouldn't have shared it if I hadn't tested those things

I admittedly have never really done anything more than rudimentary graphics programming so things like framerates etc are things WAY out of my wheelhouse ATM. I've built a career around mainly data centric tasks and automation, so 95% of the code I write for work no one EVER sees running. it just sits quietly in the dark chewing up data and spitting out data for the next tool in the chain.

There are issues with using touching block when in presentation mode
Might be better switching to using distance to neighbours instead

This seems to work for me with the blinker in presentation mode but it might have other issues

image

Like I mentioned I've coded the logic to this game in probably more languages I've forgotten than some people use on a daily basis but Unfortunately this was a case of new language and knowing WHAT it was supposed to do but not HOW to implement it. To me at least its not readily clear how to ensure a block runs just once, once for every clone, and the parallel graphics processing has me a might confused.

So sadly I am aware it is not 100% accurate to a true Conway's Game of Life, but at least in the editor it works when I run it so iterative progress :stuck_out_tongue:

I already had/have ideas on how version 2 would correct itself, and all the great ideas and feedback here are definitely going to influence those changes.

YES! THANK YOU!!

another thing I wanted to do but wasn't sure how to do it in SNAP!

This was the only logical thing I had at my mental disposal to solve the problem when I realized the odd sized boards (+ the one original sprite ) were ALL toggling the Boolean leaving me with a net zero change

Because in running it as one big `get neighbors -> keep touching -> keep wearing alive costume -> get length' especially at higher game board sizes seemed to be computationally expensive as I noted a huge slow down

Basically I'm just grabbing a small list that's literally the touching clone neighbors once when the clone is created and then just referencing it later in the forever loop. As its an array of the objects I can do a quick length of keeping the items wearing the alive costume. This is something I would do in any language I work with repeated unnecessary IO is always more expensive than data manipulation on in memory objects especially if that in memory object is internally tracking the state of the changes you care about and you only have to map or filter on them.

Yeah I actually ran it with for quite a while but put it back in last minutes because I was getting mixed results

I've got plans for a version 2 from all I've learned in the process to correct that. Now thanks to a lot of the people in this post have given me insight and i ideas on how Snap! carries out what I want it to do I'm itching to get back in and refactor some stuff.

I am completely ignorant on what this EVEN is lol

As I mentioned I really wanted this first dive into Snap! to be as pure ONLY the basics default language no tweaks, settings etc, (I think adjusting the stage size was all I ever did short of saving and opening files). That being said now that I'm feeling WAY more comfortable if my son comes to me with a question on his own Snap! projects as he is learning its time to start playing and learning things like this

Well that was purely an aesthetic choice I REALLY like how

Conways Game of Life script pic

Reads like a very simple sentence I would have called the first command block get neighbors BUT I wanted to see if it was possible to make an essentially nameless block with JUST a parameter on it

Something about looking at it an knowing someone even someone who doesn't code can go "oh I get what this is doing: 'When I start as a clone get neighbors, then forever live or die by how many neighbors are alive.' "

Having the wait out here would just muck up all that sweet sweet English language syntax.

Again this was another little experiment on my part to see if I could distill down what's visible on the main script page to English readable concepts

Thank you for the feedback I had thought about using distance but wasn't quite sure how to implement it

I had wondered why it wasn't working in presentation mode, going to add this to the bug list and potential fixes for version 2.

I'll admit my ignorance but how do I do that

I saw a place for it but its greyed out no matter where i go

Open the project in Edit mode, click the file paper icon and click "Notes".

Thank you!!

Turbo mode does all the waiting computation before updating the actual display of results on the stage. It's contraindicated in projects heavy on user interaction, such as video games, in which the display has to stay up to date with the state of the computation. But in your case, the user doesn't do anything after setting up the initial configuration. You use the block untitled script pic (2) (it looks like untitled script pic (3) in the palette) to get into and out of turbo mode. (Click in the hexagon to determine which, or you can drag a Boolean expression into it.)

You can also toggle turbo mode interactively by shift-clicking the green flag, which will look like turbo when in turbo mode.

For this project I'd be inclined to turn turbo mode on for each iteration and off in between so that every iteration is visible. As a UI frill you could have a SPEED slider that would determine how many iterations to compute before allowing a redisplay.