Leonhard ama i labirinti


25

Lo sfondo

Mio figlio Leonhard (4 anni) ama i labirinti. Non so da dove conosca i labirinti, ma li dipinge e sa abbastanza bene come funzionano:

Labirinto

Di recente, ha iniziato a creare un gioco dai suoi quadri. Queste sono le sue regole:

  • un quadrato nero indica il punto di partenza.
  • un gancio indica l'uscita del labirinto (è lì che vieni tirato fuori).
  • puoi collezionare corone.
  • puoi collezionare pepite d'oro (le cose rotonde).
  • puoi andare avanti e indietro, ma non di più.
  • le frecce possono guidarti verso l'uscita. (Se dipinge un labirinto per me da risolvere, sono spesso fuorvianti).

Versione annotata:

  • blu: punto di partenza
  • arancione: corone
  • giallo: area con pepite d'oro
  • verde: gancio (uscita)
  • rosa: frecce (per lo più fuorvianti)

Labirinto annotato

L'obiettivo

Forse sai, all'età di 4 anni, i bambini iniziano a raccontare torte di maiale e a volte non segue le proprie regole, soprattutto se scopre che non può più raggiungere la fine del labirinto.

È qui che entri in gioco: dato che cerco comunque giochi per bambini , trasformi la sua idea in un gioco in cui non è possibile imbrogliare.

Bene, abbiamo bisogno di alcune altre definizioni che direi:

  • il campo di gioco è un n* mrettangolo di quadrati di dimensioni uguali.
  • un quadrato può avere da 0 a 4 pareti, una per lato.
  • una corona vale 50 punti.
  • una pepita d'oro vale 20 punti.
  • camminare su una piazza che è già stata percorsa sottrae 1 punto.
  • i quadrati sono contrassegnati in modo da identificare la frequenza con cui il giocatore ci camminava (0, 1 o 2 volte)
  • il giocatore può camminare in 4 direzioni, tranne se c'è un muro.
  • Il dispositivo di input può essere qualsiasi cosa. Si prega di considerare il supporto della tastiera.
  • Il labirinto deve essere risolvibile. Vale a dire che deve essere possibile raggiungere l'amo dal punto di partenza e deve essere possibile raccogliere tutti gli oggetti di valore (anche se ciò non comporta il punteggio più alto possibile).
  • Se il giocatore rimane bloccato, il gioco finisce.
  • Il giocatore non deve morire cadendo dal tabellone. Puoi mettere un muro attorno al labirinto completo o avvolgere i bordi, qualunque cosa tu voglia.
  • il programma accetta come input un argomento word (0-65535). Questo è il seme per il generatore di numeri casuali. Richiamare nuovamente il programma con lo stesso seme porta allo stesso labirinto.

Bonus:

  • calcolare i punti massimi che possono essere raccolti. Considera che a causa di -1 punti potrebbe essere meglio non raccogliere tutti gli oggetti.
  • Mostra la soluzione migliore (il modo più breve per ottenere il punteggio massimo)

Le regole

Questo è un concorso di popolarità, dal momento che voglio essere in grado di leggere e comprendere il codice e forse adattarmi alle nuove idee di mio figlio. Scusate, programmate i golfisti, forse volete creare una copia di questa domanda con regole più adatte al golf, ad esempio una versione console con tutti i personaggi definiti.

Il gioco più popolare il 3 maggio diventerà la risposta accettata. E, ehi, perché non pubblicarlo in un app store?


1
Sento che questo potrebbe essere messo sotto accusa per essere troppo una competizione artistica contro una programmazione. Dal momento che tutti questi giochi dovrebbero comportarsi in modo quasi identico, quale motivo dovrei votare l'uno sull'altro oltre a quanto sia bello? Non sembra esserci molta potenziale creatività nel realizzare questo gioco del labirinto. Detto questo, sono più un giocatore di golf, quindi permetterò alle persone che più frequentemente partecipano ai pop-contro di decidere se questo è in tema.
FryAmTheEggman,

1
Ci sono due parti separate per questa sfida: una è la progettazione del labirinto (ma è difficile produrre algoritmicamente un labirinto di gioco soddisfacente) e l'altra è il motore di gioco (che è piuttosto più semplice). Penso che sarebbe meglio se limitassi la sfida al motore di gioco (e lo rendessi un codice golf.) Dovresti pubblicare alcuni labirinti di esempio (da prendere come input dal file di testo) nella domanda (possibilmente progettata con il tuo aiuto del figlio.)
Level River St

1
Correlato (e forse un duplicato parziale della parte del labirinto) codegolf.stackexchange.com/q/25967/15599
Level River St

3
Penso che questa sia una sfida eccellente. Ha diversi problemi da risolvere e spazio per la creatività. Altre sfide come questa, per favore +1. [torna alla costruzione di una soluzione ...]
Logic Knight,

Ci viene richiesto di generare "frecce". Consentono al giocatore di attraversarli dall'altra parte?
TheNumberOne il

Risposte:


16

JavaScript

Lavori in corso. Purtroppo il labirinto non è sempre risolvibile: il limite all'indietro è il vero ostacolo.

Modifica 1 Modifica cosmetica
2 Gameplay migliore, ma il grosso problema è ancora presente

var rows, columns, rowOfs, maze, plPos, points, playing

$('#BNEW').on('click', function() {
    NewMaze();
});
$('#BREP').on('click', function() {
    Start();
});

$(document).on('keydown', function(e) {
    var ok, move, pxy, mz, $td
    switch (e.which)
    {
    case 37:
        (move = -1, ok = maze[plPos+move] < 9 && maze[plPos+move*2] < 9);
        break;
    case 39:
        (move = 1, ok = maze[plPos+move] < 9 && maze[plPos+move*2] < 9);
        break;
    case 38: 
        (move = -rowOfs, ok = maze[plPos+move] < 9 && maze[plPos+move*2] < 9);
        break;
    case 40: 
        (move = rowOfs, ok = maze[plPos+move] < 9 && maze[plPos+move*2] < 9);
        break;
    }
    if (playing && ok)
    {
        pxy = getXY(plPos)
        mz = maze[plPos] < 8 ? 8 : 9;
        $td = $('#Field table tr').eq(pxy.y).find('td').eq(pxy.x)
        $td.addClass("m"+mz);
        maze[plPos] = mz;
        maze[plPos+move] = mz;
        plPos += move*2;
        mz = maze[plPos];
        PTS.value = mz==7 ? points += 20 : mz==6 ? points += 50 : mz == 8 ? points -= 1 : points;
        
        pxy = getXY(plPos)
        $td = $('#Field table tr').eq(pxy.y).find('td').eq(pxy.x)
        $td.removeClass("m6 m7")
        $('#Player').finish().animate(playerCss(pxy.x,pxy.y));
        CheckEndGame();
    }
    return !move
});

function CheckEndGame()
{
  var msg = ''
  if (maze[plPos] == 5)
  {
      msg = "Maze completed!";
  }
  else if (maze[plPos+1]==9 && maze[plPos-1]==9
        && maze[plPos+rowOfs]==9 && maze[plPos-rowOfs]==9)
  {
      msg = "You are stuck, try again!";
  }
  if (msg) {
    $("#Msg").text(msg).show();
    playing=false
  }
}

function seed(s) {
    return function() {
        s = Math.sin(s) * 10000; return s - Math.floor(s);
    };
};

function Build()
{
    var i
    var Fill = function(p)
    {
        var d=[rowOfs,-rowOfs,1,-1], i, s, l
        maze[p] = 1
        while(d[0])
        {
            i = random()*d.length|0
            s = d[i]
            if (s != (l=d.pop())) d[i]=l
            if (maze[p+s+s]>8) {
                maze[p+s] = 1, Fill(p+s+s)
            }
        }
    }
    rowOfs = (columns + 1) * 2
    maze = Array(rowOfs * (rows*2+1))
    for (i=0; i < maze.length; i++)
        maze[i] = i % rowOfs? 9 : 0
    Fill(rowOfs+2)
}

function NewMaze()
{
    SEED.value = Math.random()*65536 | 0
    Start()
}
    
function Start()
{
    var sd = SEED.value || 1
    columns = COLS.value |0 || 18
    rows = ROWS.value | 0 || 12
    COLS.value = columns
    ROWS.value = rows
    random = seed(sd)
    PTS.value = points = 0
    $("#Msg").hide();

    Build()
    
    plx = random()*columns|0;
    ply = random()*rows|0;
    setPlayer(plx,ply);
    plPos = getPos(plx,ply);
    BlockTriples(plPos);
    AddItems(plPos);
    Draw();
    playing = true;
  
}

function AddItems(p)
{
    var d=[rowOfs,-rowOfs,1,-1]
    var cells=[]
    // scan all reachable cells and calc distance from start
    var Go = function(p,l)
    {
        var i,t,mark,r
        ++l
        cells.push([p,l])
            
        maze[p] = 8;
        for(i=0; d[i]; i++)
        {
            t = d[i]
            if (maze[p+t]<2 && maze[p+t+t]<2) 
            {
                Go(p+t+t, l)
            }
        }
        maze[p] = 0;
    } 
    Go(p,0)
    cells.sort(function(a,b){return a[1]-b[1]})
    cells=cells.slice(10) // cut the shortest distances 
    r = random()*10|0; //exit
    r = cells.length-r-1
    maze[cells[r][0]] = 5;
    console.log(r)
    cells = cells.slice(0,r)
    var ncr = rows, nnu = columns
    for(i = ncr+nnu; i; i--) 
    {
        r = random()*cells.length|0
        maze[cells[r][0]] = (i > ncr ? 7 : 6);
        cells[r] = cells.pop();
    }
    
    
}

function BlockTriples(p)
{
    var d=[rowOfs,-rowOfs,1,-1]
    var Go = function(p)
    {
        var i,t,size=[0,0,0,0],s=1,nw=0,min,minp
        maze[p] = 8
        for(i=0; d[i]; i++)
        {
            t = d[i]
            if (maze[p+t]<9 && maze[p+t+t]<8) 
            {
                ++nw
                s += size[i] = Go(p+t+t)
            }
        }
        if (nw > 2) // triple way, block the smallest
        {
            for(i=0,min=1e6; d[i]; i++)
                size[i] && size[i] < min && (min = size[minp = i])
            maze[p + d[minp]] = 5;
        }
        maze[p]=0
        return s
    } 
    Go(p)
}
    
function Draw()
{    
    var q=[], i, x, y;
    for(i = rowOfs+2, y = 0; y < rows; y++)
    {
        q.push('<tr>')
        for (x = 0; x < columns; i += 2, x++)
        {
            tcl = 'm'+(maze[i]|0)       
            maze[i-1]>8 && (tcl += ' wl')
            maze[i+1]>8 && (tcl += ' wr')
            maze[i-rowOfs]>8 && (tcl += ' wu')
            maze[i+rowOfs]>8 && (tcl += ' wb')
            q.push('<td class="'+tcl+'"></td>')
        }
        q.push('</tr>')
        i += rowOfs+2
    }
    $("#TOUT").html(q.join(''));
    $("#Field").show();
}

function setPlayer(posx, posy)
{
    $('#Player').css(playerCss(posx, posy));
}    

function getXY(pos)
{
    return { x: (plPos % rowOfs)/2-1, y: plPos / rowOfs / 2 | 0}
}

function getPos(x, y)
{
   return (x+y*rowOfs)*2 + rowOfs+2;
}

function playerCss(posx, posy)
{
    return{ left: 6+posx*21, top: posy*21+2};
}
input { width: 3em; margin-right:1em }
table { 
  border: 2px solid #000; 
  border-collapse: collapse;
  font-size: 8px }
#Field { 
  margin: 10px; 
  position: relative;
  display: none;
}
td { 
    width: 19px; height: 19px; 
    padding: 0; margin: 0; 
    text-align: center;
}
td.wl { border-left: 2px solid #444; background: #fff }
td.wr { border-right: 2px solid #444; background: #fff }
td.wt { border-top: 2px solid #444;; background: #fff }
td.wb { border-bottom: 2px solid #444; background: #fff }
td.m7 { background: #ffd url(http://i.stack.imgur.com/QUInh.png) -21px 0}
td.m6 { background: #efe url(http://i.stack.imgur.com/QUInh.png) -1px 0}
td.m5 { background: #ddf url(http://i.stack.imgur.com/QUInh.png) -40px 0}
td.m8 { background: #eed }
td.m9 { background: #f55 }
#Player { position: absolute; top:0; left:0; color: #007; background: #fcb; font-size:12px }
#Msg { color: red; font-size:40px; display:none;  }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
ID:<input id=SEED>
Rows:<input id=ROWS>
Columns:<input id=COLS>
<button id=BNEW>New</button>
<button id=BREP>Replay</button>
<div id=Msg></div>
<div id=Field>
<table id=TOUT border=0 cellspacing=0 cellpadding=0>
</table>
<span id=Player></span>    
</div>
Score: <input readonly id=PTS>


Ben fatto, impressionante. 👍🏼 Non capisco la "logica" su come si crea il labirinto. Come funziona quella parte?
Cugina Cocaina,

1
@CousinCocaine La funzione Build utilizza un riempimento ricorsivo, è il modo più "classico" per costruire un labirinto semplicemente connesso - vedi en.wikipedia.org/wiki/Maze_generation_algorithm
edc65

Mio figlio lo adora. Mentre vuole che io stampi labirinti di dimensioni maggiori, gioca online con quelli più piccoli. Secondo le regole, la tua risposta è stata la migliore il 3 maggio, quindi l'ho accettata ora. Dal momento che è anche l'unica risposta, ottieni la generosità. Ben fatto, è fantastico.
Thomas Weller,

Grazie mille, felice che a tuo figlio piaccia. Ora è il momento di sollevare le offerte.
edc65,

6

Giava

Non mi sono mai lamentato di GolfScript o CJam, ma ecco comunque una risposta Java per te. Questa è stata una sfida davvero divertente. ;)

import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.geom.Ellipse2D;
import java.util.*;
import javax.swing.*;

public class MazeMaker {
    int row, col, exitRow, exitCol, numCells, score, array[][];
    final int SQWIDTH = 20;
    boolean gameOver = true;
    Ellipse2D.Double ellipse;
    JFrame frame;
    JPanel mazePanel;
    JLabel scoreLabel;

    public MazeMaker() {
        frame = new JFrame("Maze");
        frame.setLayout(new BorderLayout());
        JPanel topPanel = createTopPanel();
        frame.add(topPanel,BorderLayout.NORTH);

        createMazePanel();
        frame.add(new JScrollPane(mazePanel),BorderLayout.CENTER);

        setKeyActions();

        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.pack();
        frame.setVisible(true);
    }

    private void constructArray(int seed, int rows, int cols) {
        array = new int[rows*2-1][cols*2-1];
        for (int[] a : array)
            Arrays.fill(a,-1);
        numCells = (array.length / 2 + 1) * (array[0].length / 2 + 1);
        Random rand = new Random(seed);
        int row = rand.nextInt(array.length / 2 + 1) * 2;
        int col = rand.nextInt(array[0].length / 2 + 1) * 2;
        array[row][col] = 0;
        boolean first = true, a = false, exitFound = false;

        while (true) {
            if (first) {
                int direction = rand.nextInt(4);
                if (direction == 0 && row != 0) {
                    array[row-1][col] = 0;
                    array[row-2][col] = 0;
                    row -= 2;
                    first = false;
                }
                else if (direction == 1 && col != array[0].length - 1) {
                    array[row][col+1] = 0;
                    array[row][col+2] = 0;
                    col += 2;
                    first = false;
                }
                else if (direction == 2 && row != array.length - 1) {
                    array[row+1][col] = 0;
                    array[row+2][col] = 0;
                    row += 2;
                    first = false;
                }
                else if (direction == 3 && col != 0) {
                    array[row][col-1] = 0;
                    array[row][col-2] = 0;
                    col -= 2;
                    first = false;
                }
            }
            else {
                int availableCells = 0;
                boolean up = false, down = false, left = false, right = false;
                if (row != 0 && array[row-2][col] == -1) {
                    availableCells++;
                    up = true;
                }

                if (col != array[0].length-1 && array[row][col+2] == -1) {
                    availableCells++;
                    right = true;
                }

                if (row != array.length-1 && array[row+2][col] == -1) {
                    availableCells++;
                    down = true;
                }

                if (col != 0 && array[row][col-2] == -1) {
                    availableCells++;
                    left = true;
                }

                if (availableCells != 0) {
                    a = true;
                    while (true) {
                        boolean[] b = {up,right,down,left};
                        int i = rand.nextInt(4);
                        if (b[i]) {
                            if (i == 0) {
                                array[row-1][col] = 0;
                                array[row-2][col] = 0;
                                row -= 2;
                            }
                            else if (i == 1) {
                                array[row][col+1] = 0;
                                array[row][col+2] = 0;
                                col += 2;
                            }
                            else if (i == 2) {
                                array[row+1][col] = 0;
                                array[row+2][col] = 0;
                                row += 2;
                            }
                            else if (i == 3) {
                                array[row][col-1] = 0;
                                array[row][col-2] = 0;
                                col -= 2;
                            }
                            break;
                        }
                    }
                }
                else {
                    array[row][col] = 1;
                    if (!exitFound && a) {
                        if (new Random().nextInt(5) == 0) {
                            exitFound = true;
                            exitRow = row;
                            exitCol = col;
                        }
                    }
                    a = false;
                    if (row != 0 && array[row-1][col] == 0 && (array[row-2][col] == 0 || array[row-2][col] == 1)) {
                        array[row-1][col] = 1;
                        array[row-2][col] = 1;
                        row -= 2;
                    }
                    else if (col != array[0].length-1 && array[row][col+1] == 0 && (array[row][col+2] == 0 || array[row][col+2] == 1)) {
                        array[row][col+1] = 1;
                        array[row][col+2] = 1;
                        col += 2;
                    }
                    else if (row != array.length-1 && array[row+1][col] == 0 && (array[row+2][col] == 0 || array[row+2][col] == 1)) {
                        array[row+1][col] = 1;
                        array[row+2][col] = 1;
                        row += 2;
                    }
                    else if (col != 0 && array[row][col-1] == 0 && (array[row][col-2] == 0 || array[row][col-2] == 1)) {
                        array[row][col-1] = 1;
                        array[row][col-2] = 1;
                        col -= 2;
                    }
                }
                if (checkDone()) {
                    if (!exitFound) {
                        exitRow = row;
                        exitCol = col;
                    }
                    break;
                }
            }
        }
    }

    private boolean checkDone() {
        int count = 0;
        for (int k = 0; k < array.length; k+=2) {
            for (int l = 0; l < array[0].length; l+=2) {
                if (array[k][l] == 0 || array[k][l] == 1)
                    count++;
            }
        }
        return count == numCells;
    }

    private JPanel createTopPanel() {
        GridBagLayout l = new GridBagLayout();
        GridBagConstraints c = new GridBagConstraints();
        c.fill = GridBagConstraints.BOTH;

        JPanel panel = new JPanel(l);
        JLabel inputLabel = new JLabel("ID:");
        c.gridwidth = 1;
        l.setConstraints(inputLabel,c);
        panel.add(inputLabel);

        JTextField inputField = new JTextField(5);
        l.setConstraints(inputField,c);
        panel.add(inputField);

        JLabel rowLabel = new JLabel("Rows:");
        l.setConstraints(rowLabel,c);
        panel.add(rowLabel);

        JTextField rowField = new JTextField(3);
        l.setConstraints(rowField,c);
        panel.add(rowField);

        JLabel colLabel = new JLabel("Columns:");
        l.setConstraints(colLabel,c);
        panel.add(colLabel);

        JTextField colField = new JTextField(3);
        l.setConstraints(colField,c);
        panel.add(colField);

        JButton applyButton = new JButton("Apply");
        applyButton.addActionListener(ev -> {
            try {
                int seed = Integer.parseInt(inputField.getText()),
                    rows = Integer.parseInt(rowField.getText()),
                    cols = Integer.parseInt(colField.getText());
                if (seed >= 0 && rows >= 3 && cols >= 3) {
                    gameOver = false;
                    scoreLabel.setText("Score: " + (score = 0));
                    constructArray(seed,rows,cols);
                    row = (int) (Math.random() * (array.length / 2 + 1)) * 2;
                    col = (int) (Math.random() * (array[0].length / 2 + 1)) * 2;
                    frame.setSize((1+SQWIDTH * array[0].length)/2 > 750 ? (1+SQWIDTH * array[0].length)/2 : 750,
                            75+(1+SQWIDTH * array.length)/2);
                    mazePanel.setPreferredSize(new Dimension(
                            (1+SQWIDTH * array[0].length)/2 > 750 ? (1+SQWIDTH * array[0].length)/2 - 15 : 750,
                            15+(1+SQWIDTH * array.length)/2));
                    ellipse = new Ellipse2D.Double(col*SQWIDTH/2+3,row*SQWIDTH/2+3,10,10);
                    setItems();
                    mazePanel.repaint();
                }
            } catch (NumberFormatException ignore) {}
        });
        l.setConstraints(applyButton,c);
        panel.add(applyButton);

        scoreLabel = new JLabel("Score: ");
        c.gridwidth = GridBagConstraints.REMAINDER;
        l.setConstraints(scoreLabel,c);
        panel.add(scoreLabel);

        return panel;
    }

    private void createMazePanel() {
        mazePanel = new JPanel() {
            @Override
            protected void paintComponent(Graphics g) {
                super.paintComponent(g);
                int x = 0, y = 0;
                if (!gameOver) {
                    for (int k = 0; k < array.length; k+=2) {
                        for (int l = 0; l < array[0].length; l+=2) {
                            int n = array[k][l];
                            if (n == 0 || n == 1 || n == 4 || n == 5 || n == 6)
                                g.setColor(Color.white);
                            else if (n == 2)
                                g.setColor(Color.green);
                            else if (n == 3)
                                g.setColor(Color.red);
                            g.fillRect(x, y, SQWIDTH, SQWIDTH);
                            if (n == 4) {
                                g.setColor(new Color(245,209,34));
                                g.fillOval(x+3, y+3, 10, 10);
                            }
                            else if (n == 5) {
                                g.setColor(new Color(255,223,55));
                                g.fillPolygon(new int[]{x,x+3,x+8,x+13,x+16,x+14,x+2},new int[]{y+2,y+6,y+2,y+6,y+2,y+16,y+16},7);
                                g.setColor(new Color(12,234,44));
                                g.fillOval(x+7,y+6,4,7);
                            }
                            else if (n == 6) {
                                Graphics2D g2 = (Graphics2D) g.create();
                                g2.setStroke(new BasicStroke(2,BasicStroke.CAP_ROUND,BasicStroke.JOIN_ROUND));
                                g2.setColor(new Color(108,225,119));
                                g2.drawOval(x+5, y+1, 8, 8);
                                g2.drawLine(x+5, y+3, x+5, y+11);
                                g2.drawArc(x+5, y+8, 7, 7, 180, 180);
                            }
                            g.setColor(Color.black);
                            if (k != array.length-1 && array[k+1][l] == -1)
                                g.fillRect(x-3, y+SQWIDTH-3, SQWIDTH+3, 3);
                            if (l != array[0].length-1 && array[k][l+1] == -1)
                                g.fillRect(x+SQWIDTH-3,y,3,SQWIDTH);
                            x += SQWIDTH;
                        }
                        x = 0;
                        y += SQWIDTH;
                    }
                    g.setColor(Color.red);
                    ((Graphics2D) g).fill(ellipse);
                }
            }
        };
    }

    private void setKeyActions() {
        InputMap im = mazePanel.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
        ActionMap am = mazePanel.getActionMap();

        im.put(KeyStroke.getKeyStroke("pressed UP"), "up");
        am.put("up", new AbstractAction() {
            @Override
            public void actionPerformed(ActionEvent ev) {
                if (row != 0 && array[row-1][col] != -1 && array[row-2][col] != 3 && !gameOver) {
                    int n = array[row][col];
                    array[row][col] = n == 0 || n == 1 || n == 4 || n == 5 ? 2 : 3;
                    row -= 2;
                    n = array[row][col];
                    if (n == 4)
                        scoreLabel.setText("Score: " + (score += 20));
                    else if (n == 5)
                        scoreLabel.setText("Score: " + (score += 50));
                    else if (n == 2)
                        scoreLabel.setText("Score: " + (score -= 1));
                    ellipse.y = row * SQWIDTH/2 + 3;
                    mazePanel.repaint();
                }
                if (!gameOver && array[row][col] == 6) {
                    JOptionPane.showMessageDialog(frame, "Huzzah! You found the exit! ", "Finish", JOptionPane.PLAIN_MESSAGE);
                    gameOver = true;
                }
                else if (!gameOver && checkGameOver()) {
                    JOptionPane.showMessageDialog(frame, "You got trapped! Try again!", "Game over", JOptionPane.PLAIN_MESSAGE);
                    gameOver = true;
                }
            }
        });

        im.put(KeyStroke.getKeyStroke("pressed RIGHT"), "right");
        am.put("right",new AbstractAction() {
            @Override
            public void actionPerformed(ActionEvent ev) {
                if (col != array[0].length-1 && array[row][col+1] != -1 && array[row][col+2] != 3 && !gameOver) {
                    int n = array[row][col];
                    array[row][col] = n == 0 || n == 1 || n == 4 || n == 5 ? 2 : 3;
                    col += 2;
                    n = array[row][col];
                    if (n == 4)
                        scoreLabel.setText("Score: " + (score += 20));
                    else if (n == 5)
                        scoreLabel.setText("Score: " + (score += 50));
                    else if (n == 2)
                        scoreLabel.setText("Score: " + (score -= 1));
                    ellipse.x = col * SQWIDTH/2 + 3;
                    mazePanel.repaint();
                }
                if (!gameOver && array[row][col] == 6) {
                    JOptionPane.showMessageDialog(frame, "Huzzah! You found the exit! ", "Finish", JOptionPane.PLAIN_MESSAGE);
                    gameOver = true;
                }
                else if (!gameOver && checkGameOver()) {
                    JOptionPane.showMessageDialog(frame, "You got trapped! Try again!", "Game over", JOptionPane.PLAIN_MESSAGE);
                    gameOver = true;
                }
            }
        });

        im.put(KeyStroke.getKeyStroke("pressed DOWN"), "down");
        am.put("down", new AbstractAction() {
            @Override
            public void actionPerformed(ActionEvent ev) {
                if (row != array.length-1 && array[row+1][col] != -1 && array[row+2][col] != 3 && !gameOver) {
                    int n = array[row][col];
                    array[row][col] = n == 0 || n == 1 || n == 4 || n == 5 ? 2 : 3;
                    row += 2;
                    n = array[row][col];
                    if (n == 4)
                        scoreLabel.setText("Score: " + (score += 20));
                    else if (n == 5)
                        scoreLabel.setText("Score: " + (score += 50));
                    else if (n == 2)
                        scoreLabel.setText("Score: " + (score -= 1));
                    ellipse.y = row * SQWIDTH/2 + 3;
                    mazePanel.repaint();
                }
                if (!gameOver && array[row][col] == 6) {
                    JOptionPane.showMessageDialog(frame, "Huzzah! You found the exit! ", "Finish", JOptionPane.PLAIN_MESSAGE);
                    gameOver = true;
                }
                else if (!gameOver && checkGameOver()) {
                    JOptionPane.showMessageDialog(frame, "You got trapped! Try again!", "Game over", JOptionPane.PLAIN_MESSAGE);
                    gameOver = true;
                }
            }
        });

        im.put(KeyStroke.getKeyStroke("pressed LEFT"), "left");
        am.put("left",new AbstractAction() {
            @Override
            public void actionPerformed(ActionEvent ev) {
                if (col != 0 && array[row][col-1] != -1 && array[row][col-2] != -1 && !gameOver) {
                    int n = array[row][col];
                    array[row][col] = n == 0 || n == 1 || n == 4 || n == 5 ? 2 : 3;
                    col -= 2;
                    n = array[row][col];
                    if (n == 4)
                        scoreLabel.setText("Score: " + (score += 20));
                    else if (n == 5)
                        scoreLabel.setText("Score: " + (score += 50));
                    else if (n == 2)
                        scoreLabel.setText("Score: " + (score -= 1));
                    ellipse.x = col * SQWIDTH/2 + 3;
                    mazePanel.repaint();
                }
                if (!gameOver && array[row][col] == 6) {
                    JOptionPane.showMessageDialog(frame, "Huzzah! You found the exit! ", "Finish", JOptionPane.PLAIN_MESSAGE);
                    gameOver = true;
                }
                else if (!gameOver && checkGameOver()) {
                    JOptionPane.showMessageDialog(frame, "You got trapped! Try again!", "Game over", JOptionPane.PLAIN_MESSAGE);
                    gameOver = true;
                }
            }
        });
    }

    private void setItems() {
        array[exitRow][exitCol] = 6;
        Random r = new Random();
        for (int k = 1; k < (array.length * array[0].length) / 20; k++) {
            int row = r.nextInt(array.length / 2 + 1) * 2,
                col = r.nextInt(array[0].length / 2 + 1) * 2;
            if ((row == this.row && col == this.col) || array[row][col] == 4 || array[row][col] == 5 || array[row][col] == 6)
                k--;
            else
                array[row][col] = r.nextInt(2) + 4;
        }
    }

    private boolean checkGameOver() {
        if (row == 0 && col == 0)
            return (array[row+2][col] == 3 && array[row][col+2] == 3) ||
                   (array[row+1][col] == -1 && array[row][col+2] == 3) ||
                   (array[row+2][col] == 3 && array[row][col+1] == -1);
        else if (row == 0 && col == array[0].length-1)
            return (array[row+2][col] == 3 && array[row][col-2] == 3) ||
                   (array[row+1][col] == -1 && array[row][col-2] == 3) ||
                   (array[row+2][col] == 3 && array[row][col-1] == -1);
        else if (row == array.length-1 && col == 0)
            return (array[row-2][col] == 3 && array[row][col+2] == 3) ||
                   (array[row-1][col] == -1 && array[row][col+2] == 3) ||
                   (array[row-2][col] == 3 && array[row][col+1] == -1);
        else if (row == array.length-1 && col == array[0].length-1)
            return (array[row-2][col] == 3 && array[row][col-2] == 3) ||
                   (array[row-1][col] == -1 && array[row][col-2] == 3) ||
                   (array[row-2][col] == 3 && array[row][col-1] == -1);
        else if (row == 0)
            return (array[row+2][col] == 3 && array[row][col-1] == -1 && array[row][col+1] == -1) ||
                   (array[row+1][col] == -1 && array[row][col-2] == 3 && array[row][col+1] == -1) ||
                   (array[row+1][col] == -1 && array[row][col-1] == -1 && array[row][col+2] == 3) ||
                   (array[row+1][col] == -1 && array[row][col-2] == 3 && array[row][col+2] == 3) ||
                   (array[row+2][col] == 3 && array[row][col-2] == 3 && array[row][col+1] == -1) ||
                   (array[row+2][col] == 3 && array[row][col-1] == -1 && array[row][col+2] == 3) ||
                   (array[row+2][col] == 3 && array[row][col-2] == 3 && array[row][col+2] == 3);
        else if (col == 0)
            return (array[row][col+2] == 3 && array[row-1][col] == -1 && array[row+1][col] == -1) ||
                   (array[row][col+1] == -1 && array[row-2][col] == 3 && array[row+1][col] == -1) ||
                   (array[row][col+1] == -1 && array[row-1][col] == -1 && array[row+2][col] == 3) ||
                   (array[row][col+1] == -1 && array[row-2][col] == 3 && array[row+2][col] == 3) ||
                   (array[row][col+2] == 3 && array[row-2][col] == 3 && array[row+1][col] == -1) ||
                   (array[row][col+2] == 3 && array[row-1][col] == -1 && array[row+2][col] == 3) ||
                   (array[row][col+2] == 3 && array[row-2][col] == 3 && array[row+2][col] == 3);
        else if (row == array.length-1)
            return (array[row-2][col] == 3 && array[row][col-1] == -1 && array[row][col+1] == -1) ||
                   (array[row-1][col] == -1 && array[row][col-2] == 3 && array[row][col+1] == -1) ||
                   (array[row-1][col] == -1 && array[row][col-1] == -1 && array[row][col+2] == 3) ||
                   (array[row-1][col] == -1 && array[row][col-2] == 3 && array[row][col+2] == 3) ||
                   (array[row-2][col] == 3 && array[row][col-2] == 3 && array[row][col+1] == -1) ||
                   (array[row-2][col] == 3 && array[row][col-1] == -1 && array[row][col+2] == 3) ||
                   (array[row-2][col] == 3 && array[row][col-2] == 3 && array[row][col+2] == 3);
        else if (col == array[0].length-1)
            return (array[row][col-2] == 3 && array[row-1][col] == -1 && array[row+1][col] == -1) ||
                   (array[row][col-1] == -1 && array[row-2][col] == 3 && array[row+1][col] == -1) ||
                   (array[row][col-1] == -1 && array[row-1][col] == -1 && array[row+2][col] == 3) ||
                   (array[row][col-1] == -1 && array[row-2][col] == 3 && array[row+2][col] == 3) ||
                   (array[row][col-2] == 3 && array[row-2][col] == 3 && array[row+1][col] == -1) ||
                   (array[row][col-2] == 3 && array[row-1][col] == -1 && array[row+2][col] == 3) ||
                   (array[row][col-2] == 3 && array[row-2][col] == 3 && array[row+2][col] == 3);
        else
            return (array[row-2][col] == 3 && array[row][col+1] == -1 && array[row+1][col] == -1 && array[row][col-1] == -1) ||
                   (array[row-1][col] == -1 && array[row][col+2] == 3 && array[row+1][col] == -1 && array[row][col-1] == -1) ||
                   (array[row-1][col] == -1 && array[row][col+1] == -1 && array[row+2][col] == 3 && array[row][col-1] == -1) ||
                   (array[row-1][col] == -1 && array[row][col+1] == -1 && array[row+1][col] == -1 && array[row][col-2] == 3) ||
                   (array[row-1][col] == -1 && array[row+1][col] == -1 && array[row][col-2] == 3 && array[row][col+2] == 3) ||
                   (array[row-2][col] == 3 && array[row+2][col] == 3 && array[row][col-1] == -1 && array[row][col+1] == -1) ||
                   (array[row-2][col] == 3 && array[row+1][col] == -1 && array[row][col-1] == -1 && array[row][col+2] == 3) ||
                   (array[row-1][col] == -1 && array[row+2][col] == 3 && array[row][col-2] == 3 && array[row][col+1] == -1) ||
                   (array[row-1][col] == -1 && array[row+2][col] == 3 && array[row][col-1] == -1 && array[row][col+2] == 3) ||
                   (array[row-2][col] == 3 && array[row+1][col] == -1 && array[row][col-2] == 3 && array[row][col+1] == -1) ||
                   (array[row-1][col] == -1 && array[row+2][col] == 3 && array[row][col-2] == 3 && array[row][col+2] == 3) ||
                   (array[row-2][col] == 3 && array[row+1][col] == -1 && array[row][col-2] == 3 && array[row][col+2] == 3) ||
                   (array[row-2][col] == 3 && array[row+2][col] == 3 && array[row][col-1] == -1 && array[row][col+2] == 3) ||
                   (array[row-2][col] == 3 && array[row+2][col] == 3 && array[row][col-2] == 3 && array[row][col+1] == -1) ||
                   (array[row-2][col] == 3 && array[row+2][col] == 3 && array[row][col-2] == 3 && array[row][col+2] == 3);
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(MazeMaker::new);
    }
}

Il processo di creazione del labirinto reale utilizza l' algoritmo di ricerca approfondito , ma con un approccio iterativo. Ecco come funziona.

Iniziamo con una matrice 2D di intvalori, ogni elemento è -1. Viene scelto un elemento casuale con indici pari e il suo valore diventa 0:

-1 -1 -1 -1 -1 -1  0 -1 -1 
-1 -1 -1 -1 -1 -1 -1 -1 -1 
-1 -1 -1 -1 -1 -1 -1 -1 -1 
-1 -1 -1 -1 -1 -1 -1 -1 -1 
-1 -1 -1 -1 -1 -1 -1 -1 -1 
-1 -1 -1 -1 -1 -1 -1 -1 -1 
-1 -1 -1 -1 -1 -1 -1 -1 -1 
-1 -1 -1 -1 -1 -1 -1 -1 -1 
-1 -1 -1 -1 -1 -1 -1 -1 -1 

Quindi il programma entra in un ciclo, controllando le celle disponibili fino a raggiungere uno stato in cui non ci sono celle disponibili. Questo può accadere più volte ed è quando inizia a tornare indietro. In questo momento tutti gli 0 che incontra diventano 1. Anche durante questo periodo è quando determina se un'uscita deve essere posizionata in quella posizione. Quindi alla fine di tutto, l'array potrebbe apparire così:

 0  0  0  0  0  0  1  1  1 
 0 -1 -1 -1 -1 -1  0 -1  1 
 0  0  0  0  0 -1  0 -1  1 
-1 -1 -1 -1 -1 -1  0 -1  1 
 0  0  0  0  0  0  0 -1  1 
 0 -1 -1 -1 -1 -1 -1 -1  1 
 0 -1  1  1  1  1  1 -1  1 
 0 -1  1 -1  1 -1 -1 -1  1 
 0 -1  1 -1  1  1  1  1  1 

Quando è presente un numero predeterminato di 1 o 0 in determinati punti dell'array, il loop viene chiuso. Quindi, usando l'array, viene disegnato il labirinto risultante:

Labirinto risultante

È facile vedere che gli -1 nell'array rappresentano i muri e gli 0 e 1 sono corridoi. Gli oggetti sono distribuiti casualmente in tutto il labirinto. L'ellisse rossa è il "giocatore" che controlli.

Il labirinto è avvolto in un riquadro di scorrimento per comodità in modo che se la dimensione supera la dimensione massima della cornice è possibile scorrere per vedere il resto del labirinto.

Direi che l'unico problema con questo è come viene eseguito il controllo di fine gioco. Ho pensato a diversi modi per farlo, ma ho finito per ricorrere a codificare tutto. Sono aperto a suggerimenti su come può essere fatto.


Questo non si compila. MazeMaker.java:168: error: cannot find symbol printArray(); ^ symbol: method printArray() location: class MazeMaker
edc65,

@ edc65 Grazie per avermelo fatto notare. Avevo un metodo chiamato printArrayche utilizzavo per generare l'array, ma l'ho eliminato e ho dimenticato di rimuovere la chiamata del metodo.
TNT,

Ok, ce l'ho in esecuzione ora. È carino. Sto cercando di riavviare lo stesso labirinto mantenendo lo stesso ID, ma non sembra funzionare.
edc65,

L'ho fatto in modo che le posizioni di inizio e fine siano casuali ogni volta che si preme il pulsante Applica, ma il layout del labirinto rimane lo stesso. Non dovrebbe essere così?
TNT,

L'ho pensato come un modo per ricominciare con la stessa identica sfida. La mia preoccupazione principale è verificare che la sfida sia sempre risolvibile (un vincolo della questione originale che ho trovato difficile)
edc65,

3

Pitone

Mi rendo conto di essere in ritardo per la festa, ma ecco la mia opinione su questa sfida. Sono passato al test di testo perché non sono ancora riuscito a imparare a giocare a pigmeo. È Python 3. Cambia l'input in raw_input e dovrebbe funzionare anche in python2.

Il gancio (uscita) è rappresentato da "J". "W" è corone (50). "G" è pepite d'oro (20). Il giocatore è "O". Ho provato a usare "P" per il giocatore, ma ho trovato "O" più facile da identificare.

Ho usato la prima generazione di labirinti di profondità standard, quindi ho aggiunto l'oro, le corone, il gancio e l'attuale posizione del giocatore. Non ho implementato i criteri per ottenere tutti i tesori. Schermata del gioco del labirinto

import random

direcs = {'n':[0,-1],
          's':[0,1],
          'w':[-1,0],
          'e':[1,0]}
dirs = ['n','s','e','w']

class maze_space(object):
    def __init__(self):
        self.n = 0
        self.s = 0
        self.e = 0
        self.w = 0
        self.visited = 0
        self.contents = ' '

    def mirror(self,drct,val):
        if drct == 'n' : self.s = val
        if drct == 's' : self.n = val
        if drct == 'e' : self.w = val
        if drct == 'w' : self.e = val

class MazeGame(object):

    def __init__(self,horiz=12,vert=8):
        self.completed = 0
        self.score = 0
        self.grid = [[maze_space() for x in range(horiz)] for y in range(vert)]

        def walk(y,x):
            self.grid[y][x].visited = 1
            drs = self.make_dir_list(y, x)
            random.shuffle(drs)
            for dr in drs:
                yy = y + direcs[dr][1]
                xx = x + direcs[dr][0]
                if not self.grid[yy][xx].visited:
                    setattr(self.grid[y][x], dr, 1)
                    self.grid[yy][xx].mirror(dr, 1)
                    walk(yy, xx)

        y, x = self.pick_row_col()
        walk(y, x)
        self.maze_populate()
        self.printmaze()

    def main_loop(self):
        while not self.completed:
            move = get_move(self.grid, self.cury, self.curx)
            self.make_move(move)
            self.printmaze()

    def pick_row_col(self):
        '''pick a random cell in the grid'''
        row = random.randint(0, len(self.grid) - 1)
        col = random.randint(0, len(self.grid[0]) - 1)
        return row, col

    def make_dir_list(self, y, x):
        '''return list of available move directions'''
        drs = dirs[:]
        if x == 0 : drs.pop(drs.index('w'))
        if y == 0 : drs.pop(drs.index('n'))
        if x == len(self.grid[0]) - 1 : drs.pop(drs.index('e'))
        if y == len(self.grid) - 1 : drs.pop(drs.index('s'))
        return drs

    def maze_populate(self):
        # populate maze with crowns and gold nuggets
        for treasure in ['G', 'W']:
            for _ in range(random.randint(1, len(self.grid[0]))):
                yy, xx = self.pick_row_col()
                self.grid[yy][xx].contents = treasure

        self.cury, self.curx = self.pick_row_col() # start position
        exity, exitx = self.pick_row_col() # hook (exit) position
        #make sure start is not on top of exit
        while self.cury == exity and self.curx == exitx :
            exitx = random.randint(0, len(self.grid[0]) - 1)

        self.grid[self.cury][self.curx].contents = 'O'
        self.grid[exity][exitx].contents = 'J'

    def make_move(self,dr):
        self.grid[self.cury][self.curx].visited += 1
        self.grid[self.cury][self.curx].contents = '.'
            # player has walked twice -> disable
        if self.grid[self.cury][self.curx].visited >= 3:
            self.grid[self.cury][self.curx].contents = 'X'
            drs = self.make_dir_list(self.cury, self.curx)
            for d in drs:
                yyy = self.cury + direcs[d][1]
                xxx = self.curx + direcs[d][0]
                self.grid[yyy][xxx].mirror(d,0)

        yy = self.cury + direcs[dr][1]
        xx = self.curx + direcs[dr][0]
        if self.grid[yy][xx].contents == 'J': self.completed = 1
        if self.grid[yy][xx].contents == 'W': self.score += 50
        if self.grid[yy][xx].contents == 'G': self.score += 20
        if self.grid[yy][xx].contents == '.': self.score -= 1
        self.grid[yy][xx].contents = 'O'
        self.cury, self.curx = yy, xx

    def printmaze(self):
        if self.completed: print('\nMaze complete.  Good job!')
        print('Score: %d'%self.score)
        print('|'+''.join('---' for _ in self.grid[0]))
        for line in self.grid:
            print('|' + ''.join(' %s '%x.contents if x.e else ' %s|'%
                                x.contents for x in line))
            print('|' + ''.join('  -' if x.s else '---' for x in line))

def get_params():
    hor = input('width (default=12): ')
    hor = int(hor) if hor else 12
    ver = input('height (default=8): ')
    ver = int(ver) if ver else 8
    rseed = input('seed : ')
    rseed = int(rseed) if rseed else random.randint(1,65535)
    print(rseed)
    random.seed(rseed)
    print("'J' = hook (exit)\n'O' = Player")
    print("'G' = gold nugget (20 points)\n'W' = Crown (50 points)")
    print('Player may only tread on a given tile twice')
    return hor,ver

def get_move(grid, y, x):
    choice = input('where do you want to move %s q to quit: '%dirs)
    if choice == 'q' : exit()
    if choice in dirs and getattr(grid[y][x],choice): return choice
    return get_move(grid, y, x)

def main():
    hor,ver = get_params()
    maze = MazeGame(hor,ver)
    maze.main_loop()

if __name__ == '__main__':
    main()
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.