Problem z uruchomieniem kodu z artykułu

0

Witam
Chciałem podejrzeć jak działa kod w pythonie - ale nie znam tego języka i tu kłopot.
Gość co wrzucił kod na swego bloga poprzeplatał go tekstem i nie za bardzo wiem czy to co "skleiłem" z tych fragmentów jest prawidłowe - bo wywala błędy
Może kolejność definicji - no nie wiem :(
Oryginał : https://python.plainenglish.io/create-a-random-dungeon-with-python-f17118c1eebd
Moje wypociny :

import cairo
import random

min_room_size = 6
max_room_size = 20
max_rooms = 10
min_rooms = 3
max_iters = 3
rooms = []
map = []
map_width = 50 
map_height = 50 

class Room:
    def __init__(self,x,y,width,height):
        self.x = x
        self.y = y
        self.width = width
        self.height = height

def generate_dungeon():
    init_map()
    init_rooms()
    connect_rooms()
    draw_dungeon()   

def init_map():
    for y in range(map_height):
        for x in range(map_width):
            map[x,y] = 0
    

def __str__(self):
        return f"A room at ({self.x},{self.y})"
        


def init_rooms():
    total_rooms = randrange(min_rooms,max_rooms)
    for i in range(max_iters):
        for r in range(total_rooms):
            if len(rooms) >= max_rooms:
                break    
            x = randrange(0,map_width)
            y = randrange(0,map_height)    
            width = randrange(min_room_size,max_room_size)
            height = randrange(min_room_size,max_room_size)
            room = Room(x,y,width,height)    
            if check_for_overlap(room, rooms):
                pass
            else:
                rooms.append(room)    
    for room in rooms:
        for y in range(room.y, room.y+room.height):
            for x in range(room.x, room.x+room.width):
                map[x,y] = 1
                
def connect_rooms():
    shuffle(rooms)
    for i in range(len(rooms)-1):
        roomA = rooms[i]
        roomB = rooms[i+1]    
    for x in range(roomA.x,roomB.x):
        map[x,roomA.y] = 1
    for y in range(roomA.y, roomB.y):
        map[roomA.x,y] = 1    
    
    for x in range(roomB.x,roomA.x):
        map[x,roomA.y] = 1
    for y in range(roomB.y, roomA.y):
        map[roomA.x,y] = 1
        
def check_for_overlap(room, rooms):
    for current_room in rooms:
        xmin1 = room.x
        xmax1 = room.x + room.width
        xmin2 = current_room.x
        xmax2 = current_room.x + current_room.width
        ymin1 = room.y
        ymax1 = room.y + room.height
        ymin2 = current_room.y
        ymax2 = current_room.y + current_room.height
        if (xmin1 <= xmax2 and xmax1 >= xmin2) and (ymin1 <= ymax2 and ymax1 >= ymin2):
            return True    
    return False

def draw_dungeon():
    surface = cairo.ImageSurface(cairo.FORMAT_RGB24,500,500)
    ctx = cairo.Context(surface)    
    for y in range(50):
        for x in range(50):
            r = randrange(1,10)
            if map[x,y] == 0:
                ctx.set_source_rgb(0.3,0.3,0.3)
            else:
                ctx.set_source_rgb(0.5,0.5,0.5)
            ctx.rectangle(x*10, y*10, 10, 10)
            ctx.fill()
    surface.write_to_png("dungeon.png")
    print("Total rooms: " + str(len(rooms)))

if __name__ == "__main__":
    generate_dungeon()
1

Napisz jakie błędy albo je wklej.

1

Inicjalizujesz a nie używasz self :P

0

Błedy to :

Traceback (most recent call last):
  File "nowy.py", line 103, in <module>
    generate_dungeon()
  File "nowy.py", line 22, in generate_dungeon
    init_map()
  File "nowy.py", line 30, in init_map
    map[x,y] = 0
TypeError: list indices must be integers or slices, not tuple

@ledi12 Co masz na myśli ? - jak wspomniałem - nie znam pythona

4
titako napisał(a):
class Room:
    def __init__(self,x,y,width,height):
        self.x = x
        self.y = y
        self.width = width
        self.height = height

def generate_dungeon():
    init_map()
    init_rooms()
    connect_rooms()
    draw_dungeon()   

def init_map():
    for y in range(map_height):
        for x in range(map_width):
            map[x,y] = 0
    

def __str__(self):
        return f"A room at ({self.x},{self.y})"

Po 1sze. nadpisanie do stringa powinno byc w klasie (wcięcie jest złe -bład typa z artykułu).
Jak już poprawisz to dwie metody przenieś poniżej, żeby wyglądało tak:

class Room:

    def __init__(self,x,y,width,height):
        self.x = x
        self.y = y
        self.width = width
        self.height = height


    def __str__(self):
        return f"A room at ({self.x},{self.y})"


def generate_dungeon():
    init_map()
    init_rooms()
    connect_rooms()
    draw_dungeon()   


def init_map():
    for y in range(map_height):
        for x in range(map_width):
            map[x,y] = 0

I potem napisz co dalej nie działa, to może mi się zachce odpalić to w IDE.

p.s.
W standardowy pythonie nie da się tak indeksowac macierzy

> map[x,y] = 0

chyba po prostu olej czytanie tego kretyna

1
PanamaJoe napisał(a):

p.s.
W standardowy pythonie nie da się tak indeksowac macierzy

> map[x,y] = 0

Ale to nie jest tablica dwuwymiarowa, tylko pewnie hashmapa której kluczami jest tuple, a to akurat ma prawo zadziałać.

titako napisał(a):

Błedy to :

Traceback (most recent call last):
  File "nowy.py", line 103, in <module>
    generate_dungeon()
  File "nowy.py", line 22, in generate_dungeon
    init_map()
  File "nowy.py", line 30, in init_map
    map[x,y] = 0
TypeError: list indices must be integers or slices, not tuple

@ledi12 Co masz na myśli ? - jak wspomniałem - nie znam pythona

Twój map to jest list, a powinno pewnie być dict.

Zamień

map = []

na

map = {}
2
map = {}
map[x,y] = 0
1
TomRiddle napisał(a):
PanamaJoe napisał(a):

p.s.
W standardowy pythonie nie da się tak indeksowac macierzy

> map[x,y] = 0

Ale to nie jest tablica dwuwymiarowa, tylko pewnie hashmapa której kluczami jest tuple, a to akurat ma prawo zadziałać.

Nie. Nie jest. Wyraźnie została zdeklarowana jako tablica. Oczywiste jest dla mnie że ten pajac napisał artykuł nie odpalając nawet programu który rzekomo stworzył. Od takich pseudotutorów klejących taśmowo jakieś pseudoporadniki na sztuki to należy się trzymać z daleka. Niestety kiedyś całkiem przyzwoity agregat "medium" stał się potwornym śmietnikiem. Jak widzę to logo w górnym lewym rogu to od razu wyłączam stronę.

0
PanamaJoe napisał(a):
TomRiddle napisał(a):
PanamaJoe napisał(a):

p.s.
W standardowy pythonie nie da się tak indeksowac macierzy

> map[x,y] = 0

Ale to nie jest tablica dwuwymiarowa, tylko pewnie hashmapa której kluczami jest tuple, a to akurat ma prawo zadziałać.

Nie. Nie jest. Wyraźnie została zdeklarowana jako tablica. Oczywiste jest dla mnie że ten pajac napisał artykuł nie odpalając nawet programu który rzekomo stworzył. Od takich pseudotutorów klejących taśmowo jakieś pseudoporadniki na sztuki to należy się trzymać z daleka. Niestety kiedyś całkiem przyzwoity agregat "medium" stał się potwornym śmietnikiem. Jak widzę to logo w górnym lewym rogu to od razu wyłączam stronę.

Miałem na myśli że to w zamyśle jest hashmapa.

Rozumiem że przykro Ci że źle zdiagnozowałeś problem, ale tuple są poprawnym kluczem słownika w pythonie.

Co do pajaca z tutoriala, to pewnie należy napisać że tam w tutorialu nigdzie nie ma inicjalizcji map, więc OP sam sobie dopisał tą listę, niepoprawnie.

2
TomRiddle napisał(a):

Rozumiem że przykro Ci że źle zdiagnozowałeś problem, ale tuple są poprawnym kluczem słownika w pythonie.

Nie, problem zdiagnozowałem perfekcyjnie. We wklejonym kodzie jest to lista a nie mapa. Serio myślisz, że tylko Ty wiesz co może być poprawnym kluczem słownika pythonowego? Po co to w kółko powtarzasz? Kwestionowałem to gdzieś, że mogą być?

1

w artykule jest napisane:

Our dungeon map will be stored as a Python dictionary. We’ll use key/value pairs to associate each square of the map with x and y coordinates.

0

Dzięki Panowie

działająca wersja :

import cairo
import random

min_room_size = 6
max_room_size = 10
max_rooms = 10
min_rooms = 3
max_iters = 3
rooms = []
map = {}
map_width = 50
map_height = 50

class Room:
    def __init__(self,x,y,width,height):
        self.x = x
        self.y = y
        self.width = width
        self.height = height
    def __str__(self):
        return f"A room at ({self.x},{self.y})"
		
def generate_dungeon():
    init_map()
    init_rooms()
    connect_rooms()
    draw_dungeon()   

def init_map():
    for y in range(map_height):
        for x in range(map_width):
            map[x,y] = 0

def init_rooms():
    total_rooms = random.randrange(min_rooms,max_rooms)
    for i in range(max_iters):
        for r in range(total_rooms):
            if len(rooms) >= max_rooms:
                break    
            x = random.randrange(0,map_width)
            y = random.randrange(0,map_height)    
            width = random.randrange(min_room_size,max_room_size)
            height = random.randrange(min_room_size,max_room_size)
            room = Room(x,y,width,height)    
            if check_for_overlap(room, rooms):
                pass
            else:
                rooms.append(room)    
    for room in rooms:
        for y in range(room.y, room.y+room.height):
            for x in range(room.x, room.x+room.width):
                map[x,y] = 1
                
def connect_rooms():
    random.shuffle(rooms)
    for i in range(len(rooms)-1):
        roomA = rooms[i]
        roomB = rooms[i+1]    
    for x in range(roomA.x,roomB.x):
        map[x,roomA.y] = 1
    for y in range(roomA.y, roomB.y):
        map[roomA.x,y] = 1    
    
    for x in range(roomB.x,roomA.x):
        map[x,roomA.y] = 1
    for y in range(roomB.y, roomA.y):
        map[roomA.x,y] = 1
        
def check_for_overlap(room, rooms):
    for current_room in rooms:
        xmin1 = room.x
        xmax1 = room.x + room.width
        xmin2 = current_room.x
        xmax2 = current_room.x + current_room.width
        ymin1 = room.y
        ymax1 = room.y + room.height
        ymin2 = current_room.y
        ymax2 = current_room.y + current_room.height
        if (xmin1 <= xmax2 and xmax1 >= xmin2) and (ymin1 <= ymax2 and ymax1 >= ymin2):
            return True    
    return False

def draw_dungeon():
    surface = cairo.ImageSurface(cairo.FORMAT_RGB24,500,500)
    ctx = cairo.Context(surface)    
    for y in range(50):
        for x in range(50):
            r = random.randrange(1,10)
            if map[x,y] == 0:
                ctx.set_source_rgb(0.3,0.3,0.3)
            else:
                ctx.set_source_rgb(0.5,0.5,0.5)
            ctx.rectangle(x*10, y*10, 10, 10)
            ctx.fill()
    surface.write_to_png("dungeon.png")
    print("Total rooms: " + str(len(rooms)))

if __name__ == "__main__":
    generate_dungeon()

Generuje "coś tam" - ale nie satysfakcjonuje mnie :/ Słabo "klei" pokoje korytarzami .
Musze nad tym popracować.

0
Wibowit napisał(a):

w artykule jest napisane:

Our dungeon map will be stored as a Python dictionary. We’ll use key/value pairs to associate each square of the map with x and y coordinates.

Z obrzydzeniem sprawdziłem. A potem wypisuje bzdury w stylu "The init_map() function will create an empty map. Each square — defined by its position in the grid map — is set to a default value of 0. In our dungeon map, 0 will represent a wall and 1 will represent a room or passageway." podczas gdy próbuje wpisywać wartości do nieistniejącego obiektu (słownika). Nie proszę Szanownych Panów, w ten sposób ta funkcja wcale nie will create pusta map, bo ani ta f-cja nie zwraca mapy ani nie ma jej (mapy) nigdzie stworzonej w lub poza f-cja, którą by mogła wypełnić wartościami. Jeszcze raz: jedyne co tam mamy to próba pisania do nieistniejącego obiektu, co rzuci wyjątkiem.

0

Niby działa - jak widać z korytarzami sobie nie radzi - ale to już sobie dopracuje :)

dungeon.png

1

Dla potomnych wersja poprawiona i działająca.
Usunięte zbędne wg. mnie elementy a niektóre zmienione (korytarze).
Uwagi mile widziane - tylko bez żargonu bo nie jestem programistą ;)

import cairo
import random

min_room_size = 20
max_room_size = 50
max_rooms = 100
min_rooms = 50
rooms = []
map = {}
map_width = 300
map_height = 300

class Room:
    def __init__(self,x,y,width,height):
        self.x = x
        self.y = y
        self.width = width
        self.height = height

def generate_dungeon():
    init_map()
    init_rooms()
    connect_rooms()
    draw_dungeon()    

def init_map():
    for y in range(map_height):
        for x in range(map_width):
            map[x,y] = 0

def init_rooms():
    total_rooms = random.randrange(min_rooms,max_rooms)
    for r in range(total_rooms):
        if len(rooms) >= max_rooms:
            break
        x = random.randrange(2,map_width-max_room_size)
        y = random.randrange(2,map_height-max_room_size)
        width = random.randrange(min_room_size,max_room_size)
        height = random.randrange(min_room_size,max_room_size)
        room = Room(x,y,width,height)
        if check_for_overlap(room, rooms):
             pass
        else:
            rooms.append(room)
    for room in rooms:
        for y in range(room.y, room.y+room.height):
            for x in range(room.x, room.x+room.width):
                map[x,y] = 1
                 
def connect_rooms():
    for i in range(len(rooms)-1):
        roomA = rooms[i]
        roomB = rooms[i+1]   
        rxA = roomA.x+(roomA.width//2)
        rxB = roomB.x+(roomB.width//2)
        ryA = roomA.y+(roomA.height//2)
        ryB = roomB.y+(roomB.height//2)
        if rxA<rxB:
            xvec=1
        else:
            xvec=-1
        for x in range(rxA,rxB,xvec):
            map[x,ryA] = 1
        if ryA<ryB:
            yvec=1
        else:
            yvec=-1
        for y in range(ryA, ryB,yvec):
            map[x,y] = 1
        
def check_for_overlap(room, rooms):
    for current_room in rooms:
        xmin1 = room.x
        xmax1 = room.x + room.width
        xmin2 = current_room.x
        xmax2 = current_room.x + current_room.width
        ymin1 = room.y
        ymax1 = room.y + room.height
        ymin2 = current_room.y
        ymax2 = current_room.y + current_room.height
        if (xmin1 <= xmax2 and xmax1 >= xmin2) and (ymin1 <= ymax2 and ymax1 >= ymin2):
            return True    
    return False

def draw_dungeon():
    surface = cairo.ImageSurface(cairo.FORMAT_RGB24,map_width+8,map_height+8)
    ctx = cairo.Context(surface)    
    for y in range(map_width):
        for x in range(map_height):
            r = random.randrange(1,10)
            if map[x,y] == 0:
                ctx.set_source_rgb(0.1,0.1,0.1)
            else:
                ctx.set_source_rgb(1,1,1)
            ctx.rectangle(x+4, y+4, 1, 1)
            ctx.fill()
    surface.write_to_png("dungeon.png")
    print("Total rooms: " + str(len(rooms)))

if __name__ == "__main__":
    generate_dungeon()

A to efekt :
dungeon.png

1 użytkowników online, w tym zalogowanych: 0, gości: 1