3

I am trying to randomly generate land masses, represented by ASCII art, using Python. I have the following code which either generates mostly water or mostly land. How could I achieve my end goal?

from random import *

water = "."
land = "#"

waterChance = 40
landChance = 100 - waterChance

width = 40
height = 20

prevTile = water

level = []

for i in range(height):
    level += [[]]
    for j in range(width):

        # change to ternary 
        if prevTile == water:
            waterChance += 10
        elif prevTile == land:
            waterChance -= 10

        if(waterChance == 50):
            waterChance += 5

        tile = randrange(1,101)

        if tile > waterChance:
            print(land, end='')
            prevTile = land
        elif tile <= waterChance:
            print(water, end='')
            prevTile = water
    print()

I am looking for something like this (With the X's replaced with #s and the spaces replaced with hyphens. Sorry, I had to use ASCIIFlow to do this quickly)

    XXX
    XXXXX
   XXXXXXX
   XXXXXXXX         X
    XXXXXXX      XXXX
        XX       XXXXXX
                 XXXXXXX
                  XXXXXXX
               XXXXXXXXXX
  XXXXXX       XXXXXXX
 XXXXXXX
XXXXXXXX
XXXXXXXXXXX
 XXXXXXXXXXXXXX
  XX XXXXXXXXXXXX
  XXXXXXXXXXXXXXX
   XXXXXXXXXXXXXXX
   XXXXXXXXXXXXXXXXXX
   XXXXXXXXXXXXXXXX
    XXXXXXXXXXXX
Jordan Baron
  • 3,752
  • 4
  • 15
  • 26
  • 1
    Does `landChance` do anything in this code? Also, it seems like after `waterChance` passes 100 or goes below 0 you will get either only water or only land. It might help you to print the `waterChance` value to standard output or use some other debugging method to see how it grows as the program runs. – Nicolás Marzano Apr 20 '18 at 17:36
  • Please provide one or more examples of the sort of output you want. ASCII art land mass is vague - I've played numerous ASCII based roguelikes & many of them differ significantly with respect to drawing terrain. – Pikalek Apr 21 '18 at 13:52
  • 1
    @Pikalek Updated my post – Jordan Baron Apr 21 '18 at 14:12
  • Random fractals (e.g. random Koch Curves) are often used to generate landscapes. Such fractals can be approximated with ascii. – John Coleman Apr 21 '18 at 15:24

1 Answers1

1

Here's a loss function for connectedness.

Take total loss as ratio of land to water + some constant times the connectedness. Now you have a metric for "goodness" of a map. All of those hardcoded values in your map generator are parameters. Perform a grid search over those parameters to get high "goodness" values.

This reduces the problem to tweaking one value, the constant in your loss function. This is desirable, as now a user can trade off connectedness and land and water ratio.

EDIT Here's the connectedness function from the first link.

Program to count islands in boolean 2D matrix

class Graph:
  def __init__(self, row, col, g):
    self.ROW = row
    self.COL = col
    self.graph = g

  # A function to check if a given cell 
  # (row, col) can be included in DFS
  def isSafe(self, i, j, visited):
    # row number is in range, column number
    # is in range and value is 1 
    # and not yet visited
    return (i >= 0 and i < self.ROW and
            j >= 0 and j < self.COL and
            not visited[i][j] and self.graph[i][j])


  # A utility function to do DFS for a 2D 
  # boolean matrix. It only considers
  # the 8 neighbours as adjacent vertices
  def DFS(self, i, j, visited):

    # These arrays are used to get row and 
    # column numbers of 8 neighbours 
    # of a given cell
    rowNbr = [-1, -1, -1,  0, 0,  1, 1, 1];
        colNbr = [-1,  0,  1, -1, 1, -1, 0, 1];

    # Mark this cell as visited
    visited[i][j] = True

    # Recur for all connected neighbours
    for k in range(8):
        if self.isSafe(i + rowNbr[k], j + colNbr[k], visited):
            self.DFS(i + rowNbr[k], j + colNbr[k], visited)


  # The main function that returns
  # count of islands in a given boolean
  # 2D matrix
  def countIslands(self):
    # Make a bool array to mark visited cells.
    # Initially all cells are unvisited
    visited = [[False for j in range(self.COL)]for i in range(self.ROW)]

    # Initialize count as 0 and travese 
    # through the all cells of
    # given matrix
    count = 0
    for i in range(self.ROW):
        for j in range(self.COL):
            # If a cell with value 1 is not visited yet, 
            # then new island found
            if visited[i][j] == False and self.graph[i][j] ==1:
                # Visit all cells in this island 
                # and increment island count
                self.DFS(i, j, visited)
                count += 1

    return count

 graph = [[1, 1, 0, 0, 0],
    [0, 1, 0, 0, 1],
    [1, 0, 0, 1, 1],
    [0, 0, 0, 0, 0],
    [1, 0, 1, 0, 1]]


row = len(graph)
col = len(graph[0])

g= Graph(row, col, graph)

print "Number of islands is :"
print g.countIslands()
Mr. Polywhirl
  • 42,981
  • 12
  • 84
  • 132
Him
  • 5,257
  • 3
  • 26
  • 83
  • This answer seems highly link dependant. As a rule of thumb, if the reply doesn't supply enough information to address the problem without the links, then you need to provide additional information/details. – Pikalek Apr 21 '18 at 13:55
  • 2
    The task of generating ASCII land art is incredibly broad, and does not admit a 10 line answer. I have copied and pasted the code from one link to address your concern, but 3 times this much code is required to implement a robust solution, and stackoverflow is not a code-writing service. – Him Apr 21 '18 at 15:41