Labirent dönüş yeteneği olmadan çözme


11

Labirenti çözecek bir program yazmam gerekiyor. Labirent, her düğümün - bazı odaların ve kenarların - diğer odalara çıktığı grafik yapısına sahiptir:

resim açıklamasını buraya girin

Şartname:

  • Rastgele bir odadan başlıyoruz.
  • Labirentin çıkmaz sokakları, 0 veya daha az çıkış var.
  • Tüm labirent hakkında hiçbir şey bilmiyoruz , sadece mevcut oda sayısı ve ondan kapı listesi.
  • Yeni odaya girdiğimizde nereden geldiğimizi bilmiyoruz (mevcut odadan hangi kapı bizi önceki odaya götürüyor).
  • Çıkışa vardığımızda bunu anlayabiliriz.
  • Her adımda mevcut odadan mevcut bir kapıya geçiyoruz.
  • Örneğin 6 numaralı odadaysak, başlamak için atlayamayız. Robot hareketi gibi.
  • Mevcut odanın kimliğini tam olarak biliyoruz. Mevcut odada her kapının kimliğini biliyoruz (tüm labirentte değil).
  • Robotu kontrol ediyoruz.

Bilinen tüm algoritmalara baktım, ancak hepsi önceki odaya dönmek için en az ek yetenek gerektiriyor. Spesifikasyona göre, en kısa yolu arayan algoritmaları kullanamayız (aslında, en kısa olana ihtiyacım yok), çünkü sadece mevcut odayı biliyoruz. Sol- (Sağ-) eli algoritmalardan sonra kullanamayız, çünkü çıkışların yönünü bilmiyoruz. Muhtemelen, spesifikasyonu karşılayan tek çözüm, bir zaman çıkışının bulunacağını umarak her odada rastgele çıkış seçmektir ...

Böyle bir labirent daha akıllı bir şekilde çözmek için herhangi bir öneri?


2
Bu ödev mi?
MichaelHouse

2
Ödevlerin bir parçası. (Bunu bir şekilde işaretlemeli miyim?). Tüm odalar benzersiz bir kimliğe sahiptir.
Kyrylo M

2
Odalar çizimde bu şekilde numaralandırılmış mı? Belki daha yüksek rakamlara yönelmekle ilgili bir şey? (veya başladığınız yere bağlı olarak daha düşük)
MichaelHouse

2
Çıkış listeleri her zaman aynı sırada mı? İçinde olduğu gibi, bir harita oluşturabilir miyiz? Oda 5'deyim ve oda listesindeki 2. odaya gidiyorum, oda 4'ü buluyorum. Şimdi o oda 5-> 2'yi (oda 4) biliyorum.
MichaelHouse

4
@archer, çift gönderme size daha fazla yanıt getirebilir - şimdi, soruyu cevaplamak isteyen başka biri , farklı cevap gruplarıyla iki farklı site bulmalıdır . Bu yüzden kurallar , diğer insanların yardım almasını kolaylaştırmak için tek sorular istiyor .
Cyclops

Yanıtlar:


3

Hmm, gerçek odanın sayısını biliyorsun. Böylece bir veri yapısı oluşturabilirsiniz. Ben çıkışlar listesi ona bağlı oda numaraları yok sanırım. Ancak rastgele birini seçtikten sonra, en azından bir bağlantı olduğunu biliyorsunuz.

Oda 4'te olduğunuzu ve üç rastgele çıkıştan birini seçtiğinizi varsayalım. Şimdi sistem size oda 6'da olduğunuzu ve sadece bir çıkışınızın kaldığını söylüyor. Bunu seçiyorsunuz ve 4. odaya dönüyorsunuz. Bu noktada labirentin yapısı hakkında bilgi topladınız. Şimdi başka bir çıkış seçip oda 5'te bitiyorsunuz. Oda 4 hakkında moe bilgileri (6'ya bir çıkış, 5'e bir çıkış)

Belirli bir çıkış seçebilir misiniz? Onlar numaralandırılır, diyelim ki oda 4'te bir çıkış seçerseniz her zaman 6 ile biter misiniz? Aksi takdirde en azından olası rotalar hakkında bilgi edinebilirsiniz.

Tamam, sadece yorumunu oku. Yani çıkışların kimlikleri varsa ve bunlar bir odaya statik olarak bağlıysa (başlangıç ​​için hangisini bilmeseniz bile), sadece yeni bir tane seçersiniz ve çıkış / oda bağlantılarını hatırlar ve hangi çıkışın zaten denendiğini hatırlarsınız. Her bir odayı bulana kadar denenmemiş çıkışları deneyin.

Bu şekilde aslında basit. Birkaç adımdan sonra, az çok eksiksiz bir bağlantı listesine sahip olmalısınız. Sadece yeni bir odaya girdiğinizde (birkaç adım için) rastgele dolaşabilirsiniz. Ancak her adımda daha fazla bilgi alırsınız ve daha önce ziyaret ettiğiniz bir odaya girdiğinizde daha akıllı bir karar verebilirsiniz (test edilmemiş çıkış olmadığından örneğin çıkmaz oda 6'yı tekrar test etmeyin).

Düzenleme Fikir önce rastgele adımlar atmak ve bulduğum bilgileri açıkladığım şekilde kaydetmek (Dan'ın açıklaması daha özlüdür). Kendinizi bilinmeyen çıkışları olmayan bir odada bulursanız, henüz keşfetmediğiniz çıkışları olan bir odaya en kısa yolu bulmak için bilinen herhangi bir yol bulucuyu kullanabilirsiniz.

% 100 emin değilim, ama Peter Norvig'in "Yapay Zeka: Modern Bir Yaklaşım" adlı kitabında da benzer bir sorun (Wumpus labirent) yazdığını düşünüyorum. Doğru hatırlıyorsam, yol bulma ve sistemin komşu odalar hakkında alabileceği bazı bilgiler hakkında karar verme konusunda daha azdı.

Düzenle:

Hayır, sadece rastgele değil. Zaten denenmiş çıkışları takip ederek gereksiz yedekliliği kaldırırsınız. Aslında rastgele seçmenize bile gerek yok.

Oda 4'te başladığınızı varsayın. Aldığınız bilgiler: 3 çıkış A, B, C Her zaman kullanılmayan çıkıştan her zaman ilkini seçersiniz. Böylece A ile başlayın. Oda 6'da bitiyorsunuz. Şimdi oda 6'da 4A => 6'yı (ve kullandığınızı) hatırlıyorsunuz. 6A => 4'ü bilmek için (ve oda 6'da başka çıkış yok) Şimdi bir sonraki B çıkışını seçip oda 5'e ulaşıyorsunuz ...

Er ya da geç tüm odaları bu şekilde aramış olacaksınız. Fakat sistematik bir meselede.

Bir yol bulmaya ihtiyaç duymanızın tek nedeni, kendinizi tüm çıkışların zaten keşfedildiği bir odada bulduğunuzdadır. Ardından, aramanızla kendi başınıza gitmek için keşfedilmemiş çıkışlarla bir sonraki odaya doğrudan bir yol bulmak isteyeceksiniz. Yani ana hile hangi çıkış hangi odaya yol açtığını bilmek daha az (bu yararlı olabilir ama) ama henüz denenmemiş çıkışları takip etmek.

Bu şekilde, örneğin her zaman daireler çizmekten kaçınabilirsiniz, tamamen rasgele bir yaklaşım için risk teşkil eder.

Örnek labirentinde bu büyük olasılıkla çok fazla önemli değildir. Ancak birçok oda ve bağlantı içeren büyük bir labirentte ve belki de yuvarlak dairesel düzenlenmiş odalarda bu sistem, çıkışı mümkün olduğunca az deneme ile bulacağınızı garanti eder.

(Sanırım muhtemelen Byte56 ile aynı sistem Oyuncular'da ortaya çıktı)


Evet, odadan belirli çıkış (kapı) seçebilirim. Benzersiz kimlikleri var. Yani, öneriniz önce tam labirent keşfetmek ve daha sonra bilinen herhangi bir algoritma kullanmaktır?
Kyrylo M

Programı yazmaya başladım ve yeni bir soru aldım ... Önce tüm bağlantılar ile yapı inşa etmek için tüm odaları araştırıyorum. İkinci adım yol bulmak olacaktır. Ancak tüm bağlantılar ekleneceği zaman inşa etmeyi bırakacağım. Ama bu şekilde yine de çıkışa ulaşacağım ... Yani bu sadece rastgele bir yön
bulma

10

Muhtemelen diğer cevaplardan aldığınızın farkındayım, ama eğlenceli bir soruydu ve küçük bir Python kodlaması yapmak gibi hissettim. Nesneye yönelik yaklaşımım bu. Girinti kapsamı tanımlar.

Grafik Gösterimi

Grafik, anahtarın oda kimliği olduğu bir değer, değer sözlüğü olarak kolayca kaydedilebilir ve değer, yönlendirdiği odaların bir dizisidir.

map = {
1:[5, 2],
2:[1, 3, 5],
3:[2, 4],
4:[3, 5, 6],
5:[2, 4, 1],
6:[4]
}

Aracı Arayüzü

İlk olarak, temsilcinin çevreden hangi bilgileri öğrenebilmesi gerektiğini ve gerçekleştirebileceği işlemleri düşünmeliyiz. Bu algoritma hakkında düşünmeyi kolaylaştıracaktır.

Bu durumda ajan içinde bulunduğu odanın kimliği için ortam sorgulamak gerekir, kapıların sayısını almak mümkün olmalıdır içinde bulunduğu odada ( bu kimliği odalarının değildir notu kapılar ortaya çıkar! ) ve bir kapı endeksi belirleyerek kapıdan geçebilmelidir. Bir ajanın bildiği her şey ajanın kendisi tarafından çözülmelidir.

class AgentInterface(object):
    def __init__(self, map, starting_room):
        self.map = map
        self.current_room = starting_room

    def get_door_count(self):
        return len(self.map[self.current_room])

    def go_through_door(self, door):
        result = self.current_room = self.map[self.current_room][door]
        return result

Temsilci Bilgisi

Temsilci haritaya ilk girdiğinde, sadece odadaki kapı miktarını ve o anda bulunduğu odanın kimliğini bilir. Ajanın hangi kapılar olmadığı gibi öğrendiği bilgileri depolayacak bir yapı oluşturmam gerekiyordu. ve kapıların nereye götürdüğü burada olmuştu.

Bu sınıf tek bir oda hakkındaki bilgileri temsil eder. Görünmeyen kapıları a setve ziyaret edilen kapıları a olarak saklamayı seçtim dictionary, burada anahtar kapı kimliği ve değeri yol açtığı odanın kimliği.

class RoomKnowledge(object):
    def __init__(self, unvisited_door_count):
        self.unvisited_doors = set(range(unvisited_door_count))
        self.visited_doors = {}

Aracı algoritması

  • Temsilci bir odaya her girdiğinde, oda hakkında bilgi için bilgi sözlüğünü arar. Bu oda için herhangi bir giriş yoksa, yeni bir RoomKnowledgebilgi oluşturur ve bunu bilgi sözlüğüne ekler.

  • Mevcut odanın hedef oda olup olmadığını kontrol eder, eğer öyleyse geri döner.

  • Ziyaret etmediğimiz bu odada kapılar varsa, kapıdan geçiyoruz ve nereye götürüyoruz? Daha sonra döngüye devam ediyoruz.

  • Herhangi bir ziyaret edilmemiş kapı olmasaydı, ziyaret etmediğimiz odalara geri dönerek ziyaret edilmemiş kapıları buluyoruz.

AgentSınıf devralır AgentInterfacesınıfında.

class Agent(AgentInterface):

    def find_exit(self, exit_room_id):
        knowledge = { }
        room_history = [] # For display purposes only
        history_stack = [] # Used when we need to backtrack if we've visited all the doors in the room
        while True:
            room_knowledge = knowledge.setdefault(self.current_room, RoomKnowledge(self.get_door_count()))
            room_history.append(self.current_room)

            if self.current_room==exit_room_id:
                return room_history

            if len(room_knowledge.unvisited_doors)==0:
                # I have destination room id. I need door id:
                door = find_key(room_knowledge.visited_doors, history_stack.pop())
                self.go_through_door(door)
            else:   
                history_stack.append(self.current_room)
                # Enter the first unopened door:
                opened_door = room_knowledge.unvisited_doors.pop()
                room_knowledge.visited_doors[opened_door]=self.go_through_door(opened_door)

Destekleyici fonksiyonlar

Bir değerde verilen bir sözlükte bir anahtar bulabilecek bir işlev yazmak zorunda kaldım, çünkü geri izleme yaparken odanın kimliğini biliyoruz, ancak almak için hangi kapıyı kullanacağımızı bilmiyoruz.

def find_key(dictionary, value):
    for key in dictionary:
        if dictionary[key]==value:
            return key

Test yapmak

Yukarıda verilen haritadaki tüm başlangıç ​​/ bitiş konumu kombinasyonlarını test ettim. Her kombinasyon için ziyaret edilen odaları basar.

for start in range(1, 7):
    for exit in range(1, 7):
        print("start room: %d target room: %d"%(start,exit))
        james_bond = Agent(map, start)
        print(james_bond.find_exit(exit))

notlar

Geri izleme çok verimli değildir - en kötü senaryoda, bitişik bir odaya ulaşmak için her odadan geçebilir, ancak geri izleme oldukça nadirdir - yukarıdaki testlerde sadece üç kez geri gider. Kod özlü tutmak için istisna işleme koyarak kaçındım. Benim Python hakkında herhangi bir yorum takdir :)


Anıtsal cevap! Ne yazık ki iki cevap olamaz :( Zaten programı C # yazdım ve genellikle hemen hemen aynı fikirleri kullandım Geri izleme için Nefes derinliği arama algoritması kullanıldı.
Kyrylo M

4

Temel olarak, bağlı her odanın iki bilinmeyen geçitle bağlandığı bir yön grafiğiniz var - biri her iki yönde. Diyelim ki düğümde başlıyorsunuz 1, kapılar açılıyor Ave Bçıkıyor Her kapının ötesinde ne olduğunu bilmiyorsunuz, bu yüzden sadece kapıyı seçiyorsunuz A. Sen servise almak 2kapılar vardır, C, D, ve E. Artık o kapı biliyoruz Aodadan yol 1odasına 2rastgele kapıyı almak, böylece, ama geri almak için nasıl bilmiyorum C. Odaya geri dön 1! Şimdi odalar arasında nasıl gidileceğini 1ve 2. Çıkışı bulana kadar en yakın bilinmeyen kapıyı keşfetmeye devam edin!


4

Odalar çıkış listelerinde her zaman aynı sırada olduğundan, çıkış ararken odaların hızlı bir haritasını çıkarabiliriz.

Sahte kodum biraz Javaish, üzgünüm, son zamanlarda çok kullanıyorum.

Unvisited_Rooms, bir oda kimliği tutan bir hashmap ve eşlenmemiş odaların veya bir 2d dizisinin işe yaradığı dizinlerin bir listesidir.

Algoritmanın böyle bir şeye gidebileceğini hayal ediyorum:

Unvisited_Rooms.add(currentRoom.ID, currentRoom.exits) //add the starting room exits
while(Unvisited_Rooms.Keys.Count > 0 && currentRoom != end) //keep going while there are unmapped exits and we're not at the end
    Room1 = currentRoom
    ExitID = Room1.get_first_unmapped_Room() //returns the index of the first unmapped room
    if(ExitID == -1) //this room didn't have any more unmapped rooms, it's totally mapped
        PathTo(Get_Next_Room_With_Unmapped_Exits) //we need to go to a room with unmapped exits
        continue //we need to start over once we're there, so we don't create false links
    GoToExit(ExitID) //goes to the room, setting current room to the room on the other side
    Room1.Exits[exitID].connection = currentRoom.ID //maps the connection for later path finding
    Unvisited_Rooms[Room1.ID].remove(exitID) //removes the index so we don't worry about it
    if(Unvisited_Rooms[Room1.ID].size < 1) //checks if all the rooms exits have been accounted for
        Unvisited_Rooms.remove(Room1.ID)  //removes the room if it's exits are all mapped
    Unvisited_Rooms.add(currentRoom.ID, currentRoom.unvisited_exits) //adds more exits to the list

If(currentRoom != end && Unvisited_Rooms.Keys.Count < 1)
   print(No exit found!)
else
   print(exit is roomID: currentRoom.ID)

PathTo () odalarına "harita" boyunca ortak düğüm yolu bulucularından birini kullanmanız gerekecek. Umarım bir şeye başlamak için yeterince açıktır.


İşte bir yoklama, @ Byte56 - kaybettiğiniz onay işaretinin 2 / 3'ünü oluşturuyor. :)
Cyclops

2

Gereksinimleriniz hakkında çok net değilim, ancak aşağıdakilere izin verilirse, algoritmayı takip etmek basit olabilir. Muhtemelen orada bir hata, özellikle özyinelemeli bir işlev kullandığından. Ayrıca, bir kapının geldiğiniz odaya götürüp götürmediğini kontrol eder, ancak üç odalı dairesel bir yolun nasıl işleyeceğini bilmiyorum, sadece bu üç odada sonsuza kadar dönmeye devam edebilir. Hiçbir odanın iki kez işaretlenmediğinden emin olmak için bir geçmiş eklemeniz gerekebilir. Ancak açıklamanıza izin verilmeyebilir.

solved = FALSE

SearchRoom(rooms[0], rooms[0])    // Start at room 1 (or any room)
IF solved THEN
  // solvable
ELSE
  // unsolvable
ENDIF
END

// Recursive function, should run until it searches every room or finds the exit
FUNCTION SearchRoom: curRoom, prevRoom
  // Is this room the 'exit' room
  IF curRoom.IsExit() THEN
    solved = TRUE
    RETURN
  ENDIF

  // Deadend?  (skip starting room)
  IF (curRoom.id <> prevRoom.id) AND (curRoom.doors <= 1) THEN RETURN

  // Search each room the current room leads to
  FOREACH door IN curRoom
    // Skip the room we just came from
    IF door.id <> prevRoom.id THEN
      SearchRoom(door, curRoom)
    ENDIF
    IF solved THEN EXIT LOOP
  NEXT

  RETURN
ENDFUNCTION

[Düzenle] Önceki kontrole 'id' eklendi ve daha fazla nesne odaklı hale getirmek için güncellendi.


1
Nereden geldiğimi her adımda bilmiyorum. Yani, bu algoritmalar görevi çözmez. Ama denediğiniz için teşekkürler.
Kyrylo M

3
Yani önceki odayı bilmeye İZİN VERİLMEDİĞİNİ mi söylüyorsun? Yoksa önceki odayı bilmiyor musunuz? Yukarıdaki kod sizin için önceki odaların kaydını tutar. Bilmenize izin verilmiyorsa, labirenti 'x' deneme sayısı için rastgele yinelemekten başka geçerli bir çözüm olduğunu düşünmüyorum ve bir çıkış bulamıyorsanız, labirentin çözülemez olduğunu varsayabilirsiniz. .
Doug.McFarlane

1
"Bilmiyorum". Yine kod baktım. İkinci sorun, özyineleme kullanımının sorunlu olmasıdır. Böyle bir labirentin içinde olduğunuzu düşünün. Çıkışı bulmak için özyinelemeli algoritmayı nasıl kullanırsınız?
Kyrylo M

Ayrıca, 6. odada başlarsanız ne olur? curRoom.doors <= 1, böylece işlev bir çıkmazda olduğunu bilerek, ancak labirentin tamamını keşfettiğini düşünerek hemen geri döner.
dlras2

Bu yakındır, ancak grafikte ikiden büyük döngüler varsa yığını havaya uçurur.
mühim

1

Kısa cevap, geri izlemeyle önce derinlik aramasıdır. İsterseniz önce genişlik yapabilirsiniz, ancak küçük robotunuz ileri geri yürürken çok daha fazlasını yapacaktır.

Daha somut olarak, bize verilmiş olduğunu varsayalım:

// Moves to the given room, which must have a door between
// it and the current room.
moveTo(room);

// Returns the list of room ids directly reachable from
// the current room.
getDoors();

// Returns true if this room is the exit.
isExit();

Çıkışı bulmak için ihtiyacımız olan:

void escape(int startingRoom) {
  Stack<int> path = new Stack<int>();
  path.push(startingRoom);
  escape(path);
}

boolean escape(Stack<int> path) {
  for (int door : getDoors()) {
    // Stop if we've escaped.
    if (isExit()) return true;

    // Don't walk in circles.
    if (path.contains(door)) continue;

    moveTo(door);
    path.push(door);
    if (escape(path)) return true;

    // If we got here, the door didn't lead to an exit. Backtrack.
    path.pop();
    moveTo(path.peek());
  }
}

Aramak escape()Başlangıç ​​odasının kimliğiyle , robot çıkışı (arayarak moveTo()) çıkarır .


Bence escape(int startingRoom)aramak gerekirescape(Stack<int> path)
CiscoIPPhone

1
Bence if (path.contains(door)) continue;onun şartlarını ihlal ediyor - ajan aslında bir kapı kapıdan geçmedikçe zaten içinde bulunduğu bir odaya geri dönüp dönmediğini bilmiyor.
CiscoIPPhone

Teşekkürler, düzeltildi! Evet, şimdi gereksinimlere baktığımda, sorun biraz balık gibi görünüyor. Geri izleyemiyorsanız, umabileceğiniz en iyi şey rastgele bir yürüyüştür.
Münih
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.