Log In  

Hi! So I was researching "cellular automata" to generate tiled caves using these tutorials:

https://gamedevelopment.tutsplus.com/tutorials/generate-random-cave-levels-using-cellular-automata--gamedev-9664
https://www.youtube.com/watch?v=v7yyZZjF1z4&t=755s

I found them super useful, until I went to write my own code in LUA (I'm a novice at programming in general, so I had some trouble). I managed to complete the first part-drawing random tiles on the screen: my version is the image below. I changed the background color to white (it looked nice) and randomly tiled the screen using tile ID 001 (dark blue tile).

I started having problems when it came to applying the smoothing function (make_step()). I always ended up with a dark blue, completely tiled screen. The code I wrote is below:

--init
function _init()

  x=0
  y=0
  --screen size
  width=16
  height=16
  --chance for tile to spawn
  wall_chance=0.5

  death_limit=2
  birth_limit=5

  --init random tiles
  function random_map()
    --cycle through screen
    for x=0,width do
      for y=0,height do
        if rnd() < wall_chance then
          --draw tiles
          mset(x,y,1)
        end
      end
    end
  end

  --make smoothing stage
  function make_step()
    --cycle through screen
    for x=0,width do
      for y=0,height do
        --# of bordering tiles
        local neighbor_tiles=count_neighbors()
        --kill alive cells
        if mget(x,y)==1 then
          if neighbor_tiles<death_limit then
            mset(x,y,0)
          else
            mset(x,y,1)
          end
        --birth dead cells
        elseif mget(x,y)==0 then
          if neighbor_tiles>birth_limit then
            mset(x,y,1)
          else
            mset(x,y,0)
          end
        end      
      end
    end
  end

  --looks at 8 neighbor cells
  function count_neighbors()
    --variable we want returned
    --(# of wall tiles found)
    local count=0

    for i=-1,1 do
      for j=-1,1 do
        --cycle through points
        local neighbor_x=x+i
        local neighbor_y=y+j
        --if at center point
        if i==0 and j==0 then
        --do nothing

        --if off edge of map
        elseif neighbor_x<0
            or neighbor_y<0
            or neighbor_x>=width
            or neighbor_y>=height then
          count+=1
        --normal point check
        elseif mget(neighbor_x,neighbor_y)==1 then
          count+=1
        end
      end
    end
    return count 
  end

  --create the cave
  function generate_map()
    --simulate random screen
    random_map()

    --make smoothing stage
    for i=1,4 do
      make_step()
    end
  end

  --init cave
  generate_map()

end

--draw
function _draw()
  --draw white screen
  cls(7)
  map()
end

I've seen cellular automata done before on the Pico-8 here:

https://www.lexaloffle.com/bbs/?pid=25620 (second example cart)

However, this cart works in pixels, not tiles, and I had no idea where to even start to change the code.

If you guys have an idea as to what things I should change in my code, or know how to change the cart above from using pixels to tiles, I would be super thankful. I don't know what I'm doing half the time, so sorry if I might not understand, haha. Thanks!

Btw, sorry for the super long post.

P#73653 2020-03-04 06:21 ( Edited 2020-03-04 06:25)

Hi Rangee,

I think the main issue you're encountering is related to this;

 local neighbor_x=x+i
 local neighbor_y=y+j

These x and y variables are decalred globally and not assigned outside of your loops. Try passing in your x and y values as arguments to your neighbour chekcing function.

Additionally, you might see some interesting results applying changes to the map whilst you in the process of checking different parts of it. To ensure all cells react consistantly, apply all your changes after checking the neighbours of each cell. You could store these values in a table or (if you're really cool) you could set the pixels of your spritesheet the the number of neighbours a given cell has.

hope this helps, good luck with your cartridge!

P#73663 2020-03-04 13:48 ( Edited 2020-03-04 13:48)

Thanks so much! I passed the x and y values as arguments, and it worked great:

local neighbor_tiles=count_neighbors(x,y)
function count_neighbors(x,y)

I tested it with 2 smoothing steps, a death_limit of 3 and a birth_limit of 4. I expanded the tiles to fit the whole map, and edited the images together from each screen:

In response to your second solution, I've never tried to write map tiles to a table before, but I will definitely play around with this, even though your first solution worked great.

Thanks for your help! (I love your game Pico Pirates btw :3)

P#73672 2020-03-04 20:04

Wow, that produces really nice results. I've never tried that algorithm before. Sorry to say, but I'm glad you had an issue so that I could notice the algorithm. ;)

P#73683 2020-03-05 04:19

I'm glad you like the results! I personally only found out about it a few days ago, so I still have some fiddling around to do.

P#73694 2020-03-05 17:09

Lovely results!!

Now if you're really, REALLY cool you could look into introducing different tiles using M A R C H I N G S Q U A R E S
https://www.huderlem.com/demos/marchingsquares.html

(Thank you for the kind words on Pico Pirates, very much appreciated!)

P#73695 2020-03-05 17:55

Thanks! I'll definitely look into marching squares.
You're welcome about Pico Pirates, I especially love that you used sprite stacking!

P#73737 2020-03-06 23:03

[Please log in to post a comment]

Follow Lexaloffle:          
Generated 2024-04-18 15:26:43 | 0.008s | Q:19