Skip to content

Random Map Generator (CLI)

< Edisi command-line interface (Antarmuka berbasis teks) >

Random map generator adalah sebuah program untuk mengarang sebuah peta dan menampilkan hasilnya sebagai gambar JPG.

Sebuah peta dapat direpresentasikan sebagai sebuah matriks dengan elemen nol untuk lautan dan nilai selain nol untuk daratan.

Program ini memanfaatkan algoritma BFS untuk membuat lintasan dari simpul-simpul bernilai bukan nol yang diacak dalam sebuah larik (array) yang elemennya merupakan simpul dengan nilai nol.

1. Acak simpul-simpul tujuan untuk mengarang peta

Petunjuk Pengisian:

  • Resolusi peta (map_size): 100 s.d. 1000px
  • Ketinggian pulau maksimum (h_max): 1 s.d. 5px
  • Ketebalan dataran maksimum (b_max): 1 s.d. 5px
  • Spacing antar simpul: 5 s.d. (res_x atau res_y yang terbesar)
!pip install numpy
!pip install pillow
import numpy as np
import random as rand
from PIL import Image, ImageDraw
from IPython.display import display
from collections import deque

def intInputHandler(prompt, min, max):
    while True:
      try:
        user_input = int(input(prompt))
        if min <= user_input <= max:
          return user_input
        else:
          print(f"Input harus dalam jangkauan {min}-{max}")
      except ValueError:
        print("Input harus berupa angka!")

# Mengacak simpul-simpul yang bernilai h_max
def randomizeVertices(res_x, res_y, h_max, map_array, spacing):
    num_points_x = res_x // (spacing + 1)
    num_points_y = res_y // (spacing + 1)
    for i in range(num_points_y):
        for j in range(num_points_x):
            x = (j + 1) * (spacing + 1)
            y = (i + 1) * (spacing + 1)
            if x <= res_x and y <= res_y:
                map_array[x, y] = rand.choice([0, h_max])

map_size = int(intInputHandler("Masukkan resolusi peta: ", 100, 2000))
res_x, res_y = map_size, map_size
h_max = int(intInputHandler("Masukkan ketinggian maksimum pulau: ", 1, 5))
b_max = int(intInputHandler("Masukkan ketebalan dataran: ", 1, 5))
spacing = int(intInputHandler("Masukkan jarak antar simpul: ", 5, 100))
map_array = np.zeros((res_y, res_x), dtype=int)
randomizeVertices(res_x, res_y, h_max, map_array, spacing)
# Reset peta jika diperlukan

map_array = np.zeros((res_y, res_x), dtype=int)
randomizeVertices(res_x, res_y, h_max, map_array, spacing)

2. Buat lintasan menggunakan algoritma BFS

Membuat sebuah lintasan dari simpul-simpul tujuan yang telah diacak menggunakan algoritma breadth-first search (BFS)

def bfs(unconnected_map_array):
    visited = np.zeros_like(unconnected_map_array, dtype=bool)
    queue = deque()

    # Algoritma BFS akan terus berjalan sampai semua simpul dikunjungi
    while visited.all() is not True:

        # Cari simpul awal dengan nilai h_max untuk memulai BFS
        for i in range(res_y):
            for j in range(res_x):
                if unconnected_map_array[i, j] == h_max and not visited[i, j]:
                    queue.append(((i, j), [(i, j)]))
                    visited[i, j] = True
                    break
            if queue:
                break

        # Ketika tidak ada lagi simpul awal, maka algoritma berhenti
        if not queue:
            break

        while queue:
            current_cell, path = queue.popleft()

            # Memeriksa simpul tetangga
            for di, dj in [(0, 1), (0, -1), (1, 0), (-1, 0)]:
                ni, nj = current_cell[0] + di, current_cell[1] + dj

                # Memeriksa apakah simpul selanjutnya berada di dalam array dan belum dikunjungi
                if (0 <= ni < res_y and 0 <= nj < res_x and not visited[ni, nj]):
                    visited[ni, nj] = True
                    queue.append(((ni, nj), path + [(ni, nj)]))

                    # Jika ditemukan simpul tujuan, maka ulangi BFS dari situ
                    if unconnected_map_array[ni, nj] == h_max:
                        for i in path:
                            unconnected_map_array[i] = h_max
                        queue.clear()
                        queue.append(((ni, nj), [(ni, nj)]))

    return unconnected_map_array

connected_map_array = bfs(map_array)

3. Tinjau lintasan yang dibuat

Peninjauan lintasan yang dihasilkan dari algoritma BFS

def drawLines(res_x, res_y, h_max, b_max, connected_map_array):
    base_image = Image.new("RGB", (res_x, res_y), (0, 0, 153))
    draw = ImageDraw.Draw(base_image)

    # Menggambar titik pada setiap simpul yang dilewati lintasan
    for i in range(res_y):
        for j in range(res_x):
            if(connected_map_array[i, j] == 1):
                draw.point((i, j), fill="green")
            elif(connected_map_array[i, j] == 2):
                draw.point((i, j), fill="yellow")
            elif(connected_map_array[i, j] == 3):
                draw.point((i, j), fill="orange")
            elif(connected_map_array[i, j] == 4):
                draw.point((i, j), fill="red")
            elif(connected_map_array[i, j] == 5):
                draw.point((i, j), fill="purple")

    display(base_image)
    return base_image

base_image = drawLines(res_x, res_y, h_max, b_max, connected_map_array)

4. Gambar dan unduh peta

Klik kanan gambar yang muncul dan unduh menggunakan opsi "Simpan gambar sebagai" atau Save image as

# Ganti ketebalan dataran (b_max) jika diperlukan

b_max = int(input("Ganti b_max ke: ")) or b_max
# Beri nama peta

map_name = input("Masukkan nama peta: ") or "BFS Random Map Generator\nKelompok 6"
# Gambar datarannya saja

from PIL import Image, ImageDraw

def drawMap(res_x, res_y, h_max, b_max, connected_map_array):
    # Menambahkan tiap warna pada tiap layer agar tidak overlap
    layer0 = Image.new("RGBA", (res_x, res_y), (0, 0, 0, 0))
    grid = ImageDraw.Draw(layer0)
    draw_layer0 = ImageDraw.Draw(layer0)
    draw_grid = ImageDraw.Draw(grid)

    # Menggambar pulau :)
    for i in range(res_x):
        for j in range(res_y):
            if(connected_map_array[i][j] == h_max):
                draw_layer0.circle((i, j), rand.randint(h_max, h_max*2)*b_max/k, fill="green")

    # Menyatukan gambar awal dengan layer-layer yang dibuat
    combined_image = Image.alpha_composite(base_image.convert('RGBA'), layer0)

    image_scaling = min(1000 / res_x, 1000 / res_y)
    combined_image = combined_image.resize((int(res_x * image_scaling), int(res_y * image_scaling)), Image.NEAREST)

    layer_info = Image.new("RGBA", (int(res_x * image_scaling), int(res_y * image_scaling)), (0, 0, 0, 0))
    draw_layer_info = ImageDraw.Draw(layer_info)
    draw_layer_info.text((0,0), map_name, fill=(255,255,255,128), font_size=int(res_x * image_scaling)/30)

    combined_image = Image.alpha_composite(combined_image, layer_info)

    display(combined_image)

drawMap(res_x, res_y, h_max, b_max, connected_map_array)