[python] Gioco del 15

Discussioni su qualunque linguaggio di programmazione o engine
Rispondi
Avatar utente
doom13
Moderatore
Messaggi: 2093
Iscritto il: 31/08/2012, 15:40
Specialità: Programmazione
Uso: GM:Studio 2
Contatta:

[python] Gioco del 15

Messaggio da doom13 »

Allora ragazzi, dovrei fare questo esercizio:

Codice: Seleziona tutto

'''Consideriamo una versione ridotta del "gioco del 15" (si veda ad esempio,
http://en.wikipedia.org/wiki/15_puzzle), invece di 15 tessere in una tavola 4x4,
abbiamo 8 tessere in una tavola 3x3. Le tessere sono numerate da 1 a 8 e 0
indica la casella vuota. Un esempio di configurazione e' la seguente:

-------------
| 1 | 2 | 3 |
-------------
| 4 | 0 | 6 |
-------------
| 7 | 5 | 8 |
-------------

L'obiettivo del gioco e' di ritornare alla configurazione standard

-------------
| 1 | 2 | 3 |
-------------
| 4 | 5 | 6 |
-------------
| 7 | 8 | 0 |
-------------

potendo far scorrere ad ogni passo una tessera adiacente (in orizzontale o in
verticale) alla casella vuota, nella casella vuota. Ogni mossa puo' essere anche
vista come lo scambio della casella 0 con una delle tessere adiacenti.
Indichiamo le 4 possibili mosse con i seguenti caratteri:
'U': up, la casella 0 e' spostata in alto;
'D': down, la casella 0 e' spostate in basso
'L': left, la casella 0 e' spostata a sinistra
'R': right, la casella 0 e' spostata a destra
La configurazione sopra puo' essere portata nella configurazione standard con 2
mosse: 'D', 'R'.
Vogliamo scrivere un programma che data una configurazione trova una sequenza di
mosse ottimale (cioe' di lunghezza minima) che la riporta nella configurazione
standard. Rappresentiamo ogni configurazione con una tupla di 9 interi che
riporta i contenuti delle caselle per righe. Ad esempio, la configurazione sopra
e' rappresentata con (1,2,3,4,0,6,7,5,8) e la configurazioen standard con
(1,2,3,4,5,6,7,8,0). Per risolvere il problema possiamo considerare un grafo i
cui nodi sono tutte le possibili configurazioni (che sono 9!) e tra due nodi
c'e' un arco se e' possibile passare da l'uno all'altro tramite una mossa (si
osservi che le mosse sono simmetriche, cioe' invertibili). Per determinare una
sequenza ottimale per una configurazione basta quindi trovare un cammino di
lunghezza minima in tale grafo che collega la configurazione data a quella
standard.
Scrivere due funzioni. La prima, puzzle_tree(), deve ritornare un albero,
rappresentato come dizionario dei genitori, che ha come radice la configurazione
standard, i nodi sono tutte le configurazioni raggiungibili e ogni cammino
da una configurazione alla radice deve essere ottimale. In altre parole,
e' l'albero di visita di una BFS a partire dalla configurazione standard.
La seconda funzione, puzzle_sol(tree, cnf), preso in input un albero tree come
quello descritto sopra e una configurazione cnf, deve ritornare una lista che
contiene la sequenza di mosse che corrisponde al cammino in tree che va dalla
configurazione cnf alla radice. Se cnf non appartiene a tree, la funzione deve
ritornare None. La funzione non deve fare controlli su tree, si assume che
l'argomento tree sia un albero come sopra detto. Ad esempio,
puzzle_sol(tree, (1,2,3,4,0,6,7,5,8)) deve ritornare ['D', 'R']. La sequenza di
mosse ottimale potrebbe non essere unica, ad esempio,
puzzle_sol(tree, (6,0,4,2,1,8,5,7,3)) potrebbe ritornare la lista
['R','D','L','L','U','R','D','R','U','L','D','L','D','R','U','R','D','L','U',
 'L','U','R','R','D','D'] oppure
['L','D','D','R','R','U','U','L','L','D','R','R','U','L','L','D','R','D','R',
 'U','L','U','R','D','D']. E' sufficiente che la sequenza sia ottimale, non
importa quale tra quelle ottimali e' ritornata, stesso discorso per l'albero
ritornato da puzzle_tree().
Dovrei farlo con i grafi e questa è la classe che ho:

Codice: Seleziona tutto

class Graph(object):
    def __init__(self):
        self._nodes = {}

    def addNode(self, name, info=None):
        '''Aggiunge un nodo name, se non esiste'''
        if name not in self._nodes:
            self._nodes[name] = (set(), info)

    def addEdge(self, name1, name2):
        '''Aggiunge un arco che collega i nodi name1 e name2'''
        if not (name1 in self._nodes and name2 in self._nodes):
            return
        self._nodes[name1][0].add(name2)
        self._nodes[name2][0].add(name1)

    def adjacents(self, name):
        '''Ritorna una lista dei nomi dei nodi adiacenti al nodo name,
        se il nodo non esiste, ritorna None'''
        if name in self._nodes:
            return list(self._nodes[name][0])
        return None

    def nodes(self):
        '''Ritorna una lista dei nomi dei nodi del grafo'''
        return self._nodes.keys()

    def nodeInfo(self, name):
        '''Ritorna l'info del nodo name'''
        return self._nodes[name][1] if name in self._nodes else None
Con questa classe mi creo nodi e archi e tutto quello che mi serve.
Io stavo pensando di crearmi prima il grafo, una volta fatto, scrivere quelle due funzioni diventa molto facile però ho un bel problema.
Ieri ho scritto una funzione per crearmi il grafo ma ci mette troppo troppo ma troppo tempo. Le varie configurazioni dovrebbero essere 9! quindi anche i nodi dovrebbero essere 9! ma dopo 4 ore di lavoro aveva calcolato solo 150000 configurazioni...

Quindi la domanda è: sono io che sto sbagliando tutto e non ha senso creare il grafo con tutte le combinazioni oppure è la funzione che ho scritto che non va bene?
Ora non vi ho copiato anche la funzione che ho scritto io perché ce l'ho su un altro pc, se comunque volete dargli un'occhiata ve la copio appena posso.

Se poi volete darmi una mano con l'esercizio sarebbe fantastico ma al momento mi basta anche risolvere il problema che ho scritto sopra.
Grazie in anticipo :cappa:
Immagine
Spoiler
Maze [sospeso]
Isom (titolo provvisorio) (Windows & Android) [sospeso]
Keep Calm & Jump (Android) [In corso]
The Graywall (Windows) [Completo]
DTB (Windows & Android) [Completo]
The Last Spell (Windows) [Completo]
Dukenstein: Return to the house (Windows) [Completo]
DMSystem (Windows) [Completo]
"Things get hard sometimes guys... But remember, dicks get hard too, but they don't stay hard forever. Don't give up!"

Avatar utente
doom13
Moderatore
Messaggi: 2093
Iscritto il: 31/08/2012, 15:40
Specialità: Programmazione
Uso: GM:Studio 2
Contatta:

Re: [python] Gioco del 15

Messaggio da doom13 »

Questa è quello che ho scritto:

Codice: Seleziona tutto

#come detto questa è la classe per crearmi i grafi quindi nodi e archi
class Graph(object):
    def __init__(self):
        self._nodes = {}

    def addNode(self, name, info=None):
        '''Aggiunge un nodo name, se non esiste'''
        if name not in self._nodes:
            self._nodes[name] = (set(), info)

    def addEdge(self, name1, name2):
        '''Aggiunge un arco che collega i nodi name1 e name2'''
        if not (name1 in self._nodes and name2 in self._nodes):
            return
        self._nodes[name1][0].add(name2)
        self._nodes[name2][0].add(name1)

    def adjacents(self, name):
        '''Ritorna una lista dei nomi dei nodi adiacenti al nodo name,
        se il nodo non esiste, ritorna None'''
        if name in self._nodes:
            return list(self._nodes[name][0])
        return None

    def nodes(self):
        '''Ritorna una lista dei nomi dei nodi del grafo'''
        return self._nodes.keys()

    def nodeInfo(self, name):
        '''Ritorna l'info del nodo name'''
        return self._nodes[name][1] if name in self._nodes else None

#questa è la funzione che ho scritto
def createGraph():
    g = Graph()
    alfa = 0 #per debug
    conf_base = (1,2,3,4,5,6,7,8,0) #configurazione base
    conf_list = set() #insieme vuoto ovvero una lista vuota che non ammette duplicati
    conf_list.add(conf_base) #aggiungo all'insieme la configurazione base
    g.addNode(conf_base) #creo il primo nodo
    while len(conf_list) > 0: #finchè ci sono configurazioni da verificare in conf_list
        #prendo sempre il primo elemento della lista contente le varie configurazione
        conf = list(conf_list)[0]
        #trovo la posizione dello 0
        pos = conf.index(0)
        if pos-1 >= 0: #se esiste un numero a sinistra
            #creo la nuova combinazione
            newconf = list(conf)
            newconf[pos-1] = conf[pos]
            newconf[pos] = conf[pos-1]
            newconf = tuple(newconf)
            #creo un arco tra la configurazione precedente e la nuova
            g.addEdge(conf, newconf)
            if newconf not in g.nodes(): #se la nuova configurazione non è già presente tra i nodi
                #creo un nuovo nodo e aggiungo la nuova configurazione alla lista
                g.addNode(newconf)
                conf_list.add(newconf)

        #stessa cosa di sopra per le altre posizioni in alto, destra, in basso
        if pos-3 >= 0: #se esiste un numero in alto
            #creo la nuova combinazione
            newconf = list(conf)
            newconf[pos-3] = conf[pos]
            newconf[pos] = conf[pos-3]
            newconf = tuple(newconf)
            #creo un arco tra la configurazione precedente e la nuova
            g.addEdge(conf, newconf)
            if newconf not in g.nodes():
                #creo un nuovo nodo e aggiungo la nuova configurazione alla lista
                g.addNode(newconf)
                conf_list.add(newconf)

        if pos+1 < len(conf):
            #creo la nuova combinazione
            newconf = list(conf)
            newconf[pos+1] = conf[pos]
            newconf[pos] = conf[pos+1]
            newconf = tuple(newconf)
            #creo un arco tra la configurazione precedente e la nuova
            g.addEdge(conf, newconf)
            if newconf not in g.nodes():
                #creo un nuovo nodo e aggiungo la nuova configurazione alla lista
                g.addNode(newconf)
                conf_list.add(newconf)

        if pos+3 < len(conf):
            #creo la nuova combinazione
            newconf = list(conf)
            newconf[pos+3] = conf[pos]
            newconf[pos] = conf[pos+3]
            newconf = tuple(newconf)
            #creo un arco tra la configurazione precedente e la nuova
            g.addEdge(conf, newconf)
            if newconf not in g.nodes():
                #creo un nuovo nodo e aggiungo la nuova configurazione alla lista
                g.addNode(newconf)
                conf_list.add(newconf)

        #ora che ho controllato tutte le posizioni intorno allo 0 nella configurazione attuale e creato eventualmente le nuove combinazioni (configurazioni)
        conf_list.remove(conf) #elimino la configurazione attuale
        #----per debug
        #if len(g.nodes()) >= alfa+1000:
        #    print len(g.nodes())
        #    alfa = alfa+1000
        #----fine debug
    return g
Immagine
Spoiler
Maze [sospeso]
Isom (titolo provvisorio) (Windows & Android) [sospeso]
Keep Calm & Jump (Android) [In corso]
The Graywall (Windows) [Completo]
DTB (Windows & Android) [Completo]
The Last Spell (Windows) [Completo]
Dukenstein: Return to the house (Windows) [Completo]
DMSystem (Windows) [Completo]
"Things get hard sometimes guys... But remember, dicks get hard too, but they don't stay hard forever. Don't give up!"

Shun
Newbie
Messaggi: 2
Iscritto il: 17/10/2015, 12:11
Uso: GM:Studio 2
Contatta:

Re: [python] Gioco del 15

Messaggio da Shun »

Ciao,
mi scuso se sto per riesumare questo post ma ho lo stesso problema da risolvere e non riesco a venirne fuori :confuso:

Io per la generazione delle 9! posizioni acquisibili dai tasselli sul sistema di gioco ho pensato di precedere così :hum: :

Codice: Seleziona tutto

def permute(seq):
    '''Ritorna la lista di tutte le permutazioni della sequenza seq'''
    if len(seq) <= 1:
        perms = [seq]
    else:
       perms = []
       for i in range(len(seq)):
       # genera ricorsivamente le permutazioni degli elementi
       # escluso l'i-esimo elemento
          sub = permute(seq[:i]+seq[i+1:])
          for p in sub:     # mette in testa l'i-esimo elemento
             perms.append(seq[i:i+1]+p)
    return perms

def puzzle_tree():
    '''Implementare qui la funzione'''
    # 9! = "solo" 362880 tuple 

    seq=(1,2,3,4,5,6,7,8,0)
    perms = permute(seq)
Però credo che mi sto perdendo un pò sulla creazione degli archi tra i vari nodi così generati...
Qualche idea su come andare avanti?

Pensavo intanto di fare un controllo sulle "9!/2 = 181440" configurazioni iniziali possibili/accettate che possono portare effettivamente a una soluzione (1,2,3,4,5,6,7,8,0)

Grazie.

Avatar utente
doom13
Moderatore
Messaggi: 2093
Iscritto il: 31/08/2012, 15:40
Specialità: Programmazione
Uso: GM:Studio 2
Contatta:

Re: [python] Gioco del 15

Messaggio da doom13 »

Ciao, quello e stai facendo è l'esercizio dell'anno scorso?
Immagine
Spoiler
Maze [sospeso]
Isom (titolo provvisorio) (Windows & Android) [sospeso]
Keep Calm & Jump (Android) [In corso]
The Graywall (Windows) [Completo]
DTB (Windows & Android) [Completo]
The Last Spell (Windows) [Completo]
Dukenstein: Return to the house (Windows) [Completo]
DMSystem (Windows) [Completo]
"Things get hard sometimes guys... But remember, dicks get hard too, but they don't stay hard forever. Don't give up!"

Shun
Newbie
Messaggi: 2
Iscritto il: 17/10/2015, 12:11
Uso: GM:Studio 2
Contatta:

Re: [python] Gioco del 15

Messaggio da Shun »

Si, sto studiando e per mettermi avanti sono arrivato all'ultimo homework dell'anno passato dopo aver fatto gli altri 5 :hum:
Tu hai poi fatto tutti e tre i programmi richiesti?
Io ho fatto il primo ma secondo e terzo mi stanno creando non pochi dubbi :oops:

Per il secondo, che poi è quello del post, pensavo, dopo aver creato la lista di 9! elementi di fare un controllo di parità su tutti gli elementi per poter lasciare solo quelli che possono essere una soluzione per poi fare un controllo e creare i vari archi secondo i "movimenti" dello 0... forse così è un pò troppo complesso :confuso: :confuso:
Ho provato il tuo codice per la generazione del grafo però mi è sembrato un pò troppo "lento" :paura:


** Aggiornamento -
Ho riletto meglio il testo dell'esercizio :fapensare:
In altre parole,
e' l'albero di visita di una BFS a partire dalla configurazione standard.
Ok quindi non posso seguire la mia idea di generare tutte le possibili posizioni e poi fare un controllo di parità ma devo effettivamente generare l'albero di visita direttamente.
Mi rimetto a lavoro!

Rispondi

Chi c’è in linea

Visitano il forum: Nessuno e 27 ospiti