Author Topic: ISO simple ASCII procedural terrain  (Read 9899 times)

0 Members and 1 Guest are viewing this topic.

Offline SMcNeill

  • QB64 Developer
  • Forum Resident
  • Posts: 3972
    • View Profile
    • Steve’s QB64 Archive Forum
Re: ISO simple ASCII procedural terrain
« Reply #30 on: November 11, 2021, 02:40:10 am »
@SMcNeill

Here's feedback on where your screen ended up in my screen, the left side under my taskbar and the right side showing what's underneath about the width of the taskbar, beautiful image, your farm?

I don't detect which border you have your taskbar snapped to, so just drag the screen right a bit for your display.  At least it seems properly sized right for everything.  ;)
https://github.com/SteveMcNeill/Steve64 — A github collection of all things Steve!

Offline SMcNeill

  • QB64 Developer
  • Forum Resident
  • Posts: 3972
    • View Profile
    • Steve’s QB64 Archive Forum
Re: ISO simple ASCII procedural terrain
« Reply #31 on: November 11, 2021, 07:21:07 am »
Added Lakes into the mix.

Code: QB64: [Select]
  1. Const XSize = 200, YSize = 200
  2. Dim Shared As Long Grid(XSize, YSize)
  3.  
  4. DisplayScreen = MaxScreen
  5. Screen MaxScreen
  6.  
  7.  
  8.     InitializeMap
  9.     Lakecount = Int(Rnd * 4)
  10.     Lakes Lakecount, 400 - Lakecount * 100, 1000 - Lakecount * 300
  11.     Rivers Int(Rnd * 5) + 1, Int(Rnd * 100) - 100, -3
  12.  
  13.     GenerateTerrain
  14.  
  15.     DrawMap
  16.     Sleep
  17.  
  18. Sub InitializeMap
  19.     For x = 0 To XSize
  20.         For y = 0 To YSize
  21.             Grid(x, y) = -999 'default blank part of map
  22.         Next
  23.     Next
  24.  
  25.  
  26. Sub DrawMap
  27.     Dim kolor As _Unsigned Long
  28.     xscale = _Width / XSize
  29.     yscale = _Height / YSize
  30.     For x = 0 To XSize
  31.         For y = 0 To YSize
  32.             Select Case Grid(x, y)
  33.                 Case -3: kolor = DarkBlue 'Deep Water
  34.                 Case -2: kolor = Blue 'Water
  35.                 Case -1: kolor = SkyBlue 'Shallow Water
  36.                 Case 0: kolor = Tann 'beach/sand
  37.                 Case 1: kolor = Green 'grassland
  38.                 Case 2: kolor = DarkGreen 'forest
  39.                 Case 3: kolor = Gold 'hills
  40.                 Case 4: kolor = Purple 'mountains
  41.                 Case 5 TO 99: kolor = Red
  42.                 Case Else: kolor = Black
  43.             End Select
  44.             Line (x * xscale, y * yscale)-Step(xscale, yscale), kolor, BF
  45.     Next y, x
  46.  
  47.  
  48. Sub GenerateTerrain
  49.     Height = -3
  50.     Do Until finished
  51.         finished = -1
  52.         For x = 0 To XSize
  53.             For y = 0 To YSize
  54.                 If Grid(x, y) = Height Then Fill x, y, Height + 1: finished = 0
  55.             Next
  56.         Next
  57.         Height = Height + 1
  58.     Loop
  59.  
  60.  
  61. Sub Fill (x, y, height)
  62.     Select Case height
  63.         Case Is = -2: RepeatChance = 50 'water repeat
  64.         Case Is = -1: RepeatChance = 30 'shallow water repeat
  65.         Case Is = 0: RepeatChance = 25 'beach repeat
  66.         Case Is = 1: RepeatChance = 55 'grassland
  67.         Case Is = 2: RepeatChance = 55 'forest
  68.         Case Is = 3: RepeatChance = 50 ' hills
  69.         Case Is = 4: RepeatChance = 50 'mountains
  70.         Case Else
  71.             RepeatChance = 50 - 3 * height
  72.             If RepeatChance < 10 Then RepeatChance = 10
  73.     End Select
  74.     CurrentX = x
  75.     If CurrentX > 0 Then
  76.         If Grid(CurrentX - 1, y) = -999 Then
  77.             Grid(CurrentX - 1, y) = height
  78.             If Int(Rnd * 100) < RepeatChance Then Fill CurrentX - 1, y, height
  79.         End If
  80.     End If
  81.     CurrentX = x
  82.     If CurrentX < XSize Then
  83.         If Grid(CurrentX + 1, y) = -999 Then
  84.             Grid(CurrentX + 1, y) = height
  85.             If Int(Rnd * 100) < RepeatChance Then Fill CurrentX + 1, y, height
  86.         End If
  87.     End If
  88.     CurrentY = y
  89.     If CurrentY > 0 Then
  90.         If Grid(x, CurrentY - 1) = -999 Then
  91.             Grid(x, CurrentY - 1) = height
  92.             If Int(Rnd * 100) < RepeatChance Then Fill x, CurrentY - 1, height
  93.         End If
  94.     End If
  95.     CurrentY = y
  96.     If CurrentY < YSize Then
  97.         If Grid(x, CurrentY + 1) = -999 Then
  98.             Grid(x, y + 1) = height
  99.             If Int(Rnd * 100) < RepeatChance Then Fill x, CurrentY + 1, height
  100.         End If
  101.     End If
  102.  
  103. Sub Lakes (Number, MinSize, MaxSize)
  104.     For i = 1 To Number
  105.         x = Int(Rnd * XSize): y = Int(Rnd * YSize)
  106.         LakeSize = Int(Rnd * (MaxSize - MinSize)) + MinSize
  107.         LakeBuilt = 0
  108.         Do Until LakeBuilt >= LakeSize
  109.             xchange = 0: ychange = 0
  110.             Do
  111.                 Do
  112.                     xchange = Int(Rnd * 3) - 1
  113.                 Loop Until x + xchange > 0 And x + xchange < XSize
  114.                 Do
  115.                     ychange = Int(Rnd * 3) - 1
  116.                 Loop Until y + ychange > 0 And y + ychange < YSize
  117.             Loop Until xchange <> 0 And ychange <> 0
  118.             repeat:
  119.             If x + xchange < 0 Or x + xchange > XSize Then xchange = -xchange
  120.             If y + ychange < 0 Or y + ychange > YSize Then ychange = -ychange
  121.             If Grid(x + xchange, y + ychange) = -999 Then
  122.                 Grid(x + xchange, y + ychange) = -3
  123.                 LakeBuilt = LakeBuilt + 1
  124.                 x = x + xchange: y = y + ychange
  125.             Else
  126.                 flip = Int(Rnd * 2)
  127.                 If flip Then xchange = xchange * 2 Else ychange = ychange * 2
  128.                 GoTo repeat
  129.             End If
  130.         Loop
  131.     Next
  132.  
  133. Sub Rivers (Number, Meander, Deep)
  134.     For i = 1 To Number
  135.         flip1 = Int(Rnd * 2): flip2 = Int(Rnd * 2)
  136.         If flip1 Then 'entry point is on top
  137.             x1 = Int(Rnd * XSize): y1 = 0
  138.         Else 'entry point is on left
  139.             x1 = 0: y1 = Int(Rnd * YSize)
  140.         End If
  141.         If flip2 Then 'exit point is on bottom
  142.             x2 = Int(Rnd * XSize): y2 = YSize
  143.         Else 'exit point is on right
  144.             x2 = XSize: y2 = Int(Rnd * YSize)
  145.         End If
  146.  
  147.         Grid(x1, y1) = Deep: Grid(x2, y2) = Deep
  148.         StartX = x1: StartY = y1: EndX = x2: EndY = y2 'just to preserve our original values, if needed.
  149.         Do Until StartX = EndX And StartY = EndY
  150.             CoinToss = Int(Rnd * 100) 'Coin toss to move left/right or up/down, to go towards exit, or wander a bit.
  151.             Meander = 10
  152.             If CoinToss Mod 2 Then 'even or odd, so we only walk vertical or hortizontal and not diagional
  153.                 If CoinToss < 100 - Meander Then 'Lower values meander less and go directly to the target.
  154.                     XChange = Sgn(EndX - StartX) '-1,0,1, drawn always towards the mouse
  155.                     Ychange = 0
  156.                 Else
  157.                     XChange = Int(Rnd * 3) - 1 '-1, 0, or 1, drawn in a random direction to let the lightning wander
  158.                     Ychange = 0
  159.                 End If
  160.             Else
  161.                 If CoinToss < 100 - Meander Then 'Lower values meander less and go directly to the target.
  162.                     Ychange = Sgn(EndY - StartY)
  163.                     XChange = 0
  164.                 Else
  165.                     Ychange = Int(Rnd * 3) - 1
  166.                     XChange = 0
  167.                 End If
  168.             End If
  169.             StartX = StartX + XChange
  170.             StartY = StartY + Ychange
  171.             If StartX < 0 Then StartX = 0 'Make certain we move inside the bounds of our map dimensions
  172.             If StartY < 0 Then StartY = 0
  173.             If StartX > XSize Then StartX = XSize
  174.             If StartY > YSize Then StartY = YSize
  175.             Grid(StartX, StartY) = Deep 'place a river where we moved to
  176.         Loop
  177.     Next
  178.  
  179.  
  180.  
  181.  
  182.  
  183. Function MaxScreen
  184.     DH = _DesktopHeight: DW = _DesktopWidth
  185.     TBH = TaskbarHeight: TBW = TaskbarWidth
  186.     TH = TitleBarHeight: BW = BorderWidth
  187.     If TBH = DH Then TBH = 0 'Users taskbar is configured vertical, not hortizonal.
  188.     If TBW = DW Then TBW = 0
  189.     MaxScreen = _NewImage(DW - TBW - 2 * BW, DH - TBH - TH - BW * 2, 32)
  190.  
  191. Sub ScreenMove (x, y)
  192.     Do Until _Width <> 0 And _ScreenExists = -1: Loop
  193.     _ScreenMove x - BorderWidth, y - BorderWidth - TitleBarHeight
  194.  
  195. Sub ScreenMove_Middle
  196.     Do Until _Width <> 0 And _ScreenExists = -1: Loop
  197.     _ScreenMove (_DesktopWidth - _Width - BorderWidth) / 2 + 1, (_DesktopHeight - _Height - BorderWidth) / 2 - TitleBarHeight + 1
  198.  
  199. Function TaskbarHeight
  200.     Do Until _Width <> 0 And _ScreenExists = -1: Loop
  201.  
  202.     $If WIN Then
  203.         $If TASKBARDEC = UNDEFINED Then
  204.             $Let TASKBARDEC = TRUE
  205.             Declare Library "taskbar"
  206.                 Function taskbar_height& ()
  207.                 Function taskbar_width& ()
  208.             End Declare
  209.         $End If
  210.         TaskbarHeight = taskbar_height&
  211.     $Else
  212.         TaskbarHeight = 0 'no function to get the value for Linux/Mac, so return 0 instead of an error
  213.     $End If
  214.  
  215. Function TaskbarWidth
  216.     Do Until _Width <> 0 And _ScreenExists = -1: Loop
  217.     $If WIN Then
  218.         $If TASKBARDEC = UNDEFINED Then
  219.             $Let TASKBARDEC = TRUE
  220.  
  221.             Declare Library "taskbar"
  222.             Function taskbar_height& ()
  223.             Function taskbar_width& ()
  224.             End Declare
  225.         $End If
  226.         TaskbarWidth = taskbar_width&
  227.     $Else
  228.         TaskbarWidth = 0 'no function to get the value for Linux/Mac, so return 0 instead of an error
  229.     $End If
  230.  
  231.  
  232. Function TitleBarHeight
  233.     Do Until _Width <> 0 And _ScreenExists = -1: Loop
  234.     $If BORDERDEC = UNDEFINED Then
  235.         $Let BORDERDEC = TRUE
  236.         Declare Library
  237.             Function glutGet& (ByVal what&)
  238.         End Declare
  239.     $End If
  240.     TitleBarHeight = glutGet(507)
  241.  
  242. Function BorderWidth
  243.     Do Until _Width <> 0 And _ScreenExists = -1: Loop
  244.     $If BORDERDEC = UNDEFINED Then
  245.         $Let BORDERDEC = TRUE
  246.         Declare Library
  247.         Function glutGet& (ByVal what&)
  248.         End Declare
  249.     $End If
  250.     BorderWidth = glutGet(506)

  [ You are not allowed to view this attachment ]  

  [ You are not allowed to view this attachment ]  

I've got to admit, I think some of these end up looking rather nice.  (Of course, since almost everything is random here, some of these end up looking like complete garbage to me as well.)

Keep in mind, I'm creating massive 200 x 200 world maps with the settings the way I currently have them.  Also note, the actual game would probably be at a much larger scale with only small portions of it viewable by the player at a time.   I also don't know if I'd bother to use so many colors for water... Probably just shallow water (where you can wade in it) and deep water (where a boat travels) would be good enough.  My thinking behind 3 levels of water here was basically ocean ship, canoe/raft/shallow drag boat, and then wading/shallow water.

Anywho...   I'd call this a decent shot at a random terrain generator.  It doesn't follow any basic rules of logic, but it's decent enough I'm not ashamed to share it.  :P

If I was serious about this thing, I'd probably start at my mountains and then flow down to my oceans and not backwards like I did in this attempt, as that seems like it'd generate a more natural water flow from high to low.  I'd also try to work in things like temperature zones for the polar regions, and deserts for places which are too far away from any major source of water and would normally be plains instead.

Enough to showcase the basic idea behind things here, but it can definitely be expanded on if someone was wanting to.  ;D
https://github.com/SteveMcNeill/Steve64 — A github collection of all things Steve!

Offline xra7en

  • Seasoned Forum Regular
  • Posts: 284
    • View Profile
Re: ISO simple ASCII procedural terrain
« Reply #32 on: November 11, 2021, 11:07:49 pm »
still....

you have something there.

gonna tinker with it, and post a couple of images. I think this might work for what I am looking for. Will have to do a little converting to ASCII (piece o cake :P)

wonder how Nox got MC almost perfect generating land! ??
I just like re-writing old DOS book games into modern QB64 code - weird hobby, I know!