Task: Sokoban



C#: using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace SokobanSolver
{
    public class SokobanSolver
    {
        private class Board
        {
            public string Cur { get; internal set; }
            public string Sol { get; internal set; }
            public int X { get; internal set; }
            public int Y { get; internal set; }

            public Board(string cur, string sol, int x, int y)
            {
                Cur = cur;
                Sol = sol;
                X = x;
                Y = y;
            }
        }

        private string destBoard, currBoard;
        private int playerX, playerY, nCols;

        SokobanSolver(string[] board)
        {
            nCols = board[0].Length;
            StringBuilder destBuf = new StringBuilder();
            StringBuilder currBuf = new StringBuilder();

            for (int r = 0; r < board.Length; r++)
            {
                for (int c = 0; c < nCols; c++)
                {

                    char ch = board[r][c];

                    destBuf.Append(ch != '$' && ch != '@' ? ch : ' ');
                    currBuf.Append(ch != '.' ? ch : ' ');

                    if (ch == '@')
                    {
                        this.playerX = c;
                        this.playerY = r;
                    }
                }
            }
            destBoard = destBuf.ToString();
            currBoard = currBuf.ToString();
        }

        private string Move(int x, int y, int dx, int dy, string trialBoard)
        {

            int newPlayerPos = (y + dy) * nCols + x + dx;

            if (trialBoard[newPlayerPos] != ' ')
                return null;

            char[] trial = trialBoard.ToCharArray();
            trial[y * nCols + x] = ' ';
            trial[newPlayerPos] = '@';

            return new string(trial);
        }

        private string Push(int x, int y, int dx, int dy, string trialBoard)
        {

            int newBoxPos = (y + 2 * dy) * nCols + x + 2 * dx;

            if (trialBoard[newBoxPos] != ' ')
                return null;

            char[] trial = trialBoard.ToCharArray();
            trial[y * nCols + x] = ' ';
            trial[(y + dy) * nCols + x + dx] = '@';
            trial[newBoxPos] = '$';

            return new string(trial);
        }

        private bool IsSolved(string trialBoard)
        {
            for (int i = 0; i < trialBoard.Length; i++)
                if ((destBoard[i] == '.')
                        != (trialBoard[i] == '$'))
                    return false;
            return true;
        }

        private string Solve()
        {
            char[,] dirLabels = { { 'u', 'U' }, { 'r', 'R' }, { 'd', 'D' }, { 'l', 'L' } };
            int[,] dirs = { { 0, -1 }, { 1, 0 }, { 0, 1 }, { -1, 0 } };
            ISet<string> history = new HashSet<string>();
            LinkedList<Board> open = new LinkedList<Board>();

            history.Add(currBoard);
            open.AddLast(new Board(currBoard, string.Empty, playerX, playerY));

            while (!open.Count.Equals(0))
            {
                Board item = open.First();
                open.RemoveFirst();
                string cur = item.Cur;
                string sol = item.Sol;
                int x = item.X;
                int y = item.Y;

                for (int i = 0; i < dirs.GetLength(0); i++)
                {
                    string trial = cur;
                    int dx = dirs[i, 0];
                    int dy = dirs[i, 1];

                    
                    if (trial[(y + dy) * nCols + x + dx] == '$')
                    {
                        
                        if ((trial = Push(x, y, dx, dy, trial)) != null)
                        {
                            
                            if (!history.Contains(trial))
                            {

                                string newSol = sol + dirLabels[i, 1];

                                if (IsSolved(trial))
                                    return newSol;

                                open.AddLast(new Board(trial, newSol, x + dx, y + dy));
                                history.Add(trial);
                            }
                        }
                        
                    }
                    else if ((trial = Move(x, y, dx, dy, trial)) != null)
                    {
                        if (!history.Contains(trial))
                        {
                            string newSol = sol + dirLabels[i, 0];
                            open.AddLast(new Board(trial, newSol, x + dx, y + dy));
                            history.Add(trial);
                        }
                    }
                }
            }
            return "No solution";
        }

        public static void Main(string[] a)
        {
            string level = "#######," +
                           "#     #," +
                           "#     #," +
                           "#. #  #," +
                           "#. $$ #," +
                           "#.$$  #," +
                           "#.#  @#," +
                           "#######";
            System.Console.WriteLine("Level:\n");
            foreach (string line in level.Split(','))
            {
                System.Console.WriteLine(line);
            }
            System.Console.WriteLine("\nSolution:\n");
            System.Console.WriteLine(new SokobanSolver(level.Split(',')).Solve());
        }
    }
}



Java: import java.util.*;

public class Sokoban {
    String destBoard, currBoard;
    int playerX, playerY, nCols;

    Sokoban(String[] board) {
        nCols = board[0].length();
        StringBuilder destBuf = new StringBuilder();
        StringBuilder currBuf = new StringBuilder();

        for (int r = 0; r < board.length; r++) {
            for (int c = 0; c < nCols; c++) {

                char ch = board[r].charAt(c);

                destBuf.append(ch != '$' && ch != '@' ? ch : ' ');
                currBuf.append(ch != '.' ? ch : ' ');

                if (ch == '@') {
                    this.playerX = c;
                    this.playerY = r;
                }
            }
        }
        destBoard = destBuf.toString();
        currBoard = currBuf.toString();
    }

    String move(int x, int y, int dx, int dy, String trialBoard) {

        int newPlayerPos = (y + dy) * nCols + x + dx;

        if (trialBoard.charAt(newPlayerPos) != ' ')
            return null;

        char[] trial = trialBoard.toCharArray();
        trial[y * nCols + x] = ' ';
        trial[newPlayerPos] = '@';

        return new String(trial);
    }

    String push(int x, int y, int dx, int dy, String trialBoard) {

        int newBoxPos = (y + 2 * dy) * nCols + x + 2 * dx;

        if (trialBoard.charAt(newBoxPos) != ' ')
            return null;

        char[] trial = trialBoard.toCharArray();
        trial[y * nCols + x] = ' ';
        trial[(y + dy) * nCols + x + dx] = '@';
        trial[newBoxPos] = '$';

        return new String(trial);
    }

    boolean isSolved(String trialBoard) {
        for (int i = 0; i < trialBoard.length(); i++)
            if ((destBoard.charAt(i) == '.')
                    != (trialBoard.charAt(i) == '$'))
                return false;
        return true;
    }

    String solve() {
        class Board {
            String cur, sol;
            int x, y;

            Board(String s1, String s2, int px, int py) {
                cur = s1;
                sol = s2;
                x = px;
                y = py;
            }
        }
        char[][] dirLabels = {{'u', 'U'}, {'r', 'R'}, {'d', 'D'}, {'l', 'L'}};
        int[][] dirs = {{0, -1}, {1, 0}, {0, 1}, {-1, 0}};

        Set<String> history = new HashSet<>();
        LinkedList<Board> open = new LinkedList<>();

        history.add(currBoard);
        open.add(new Board(currBoard, "", playerX, playerY));

        while (!open.isEmpty()) {
            Board item = open.poll();
            String cur = item.cur;
            String sol = item.sol;
            int x = item.x;
            int y = item.y;

            for (int i = 0; i < dirs.length; i++) {
                String trial = cur;
                int dx = dirs[i][0];
                int dy = dirs[i][1];

                
                if (trial.charAt((y + dy) * nCols + x + dx) == '$') {

                    
                    if ((trial = push(x, y, dx, dy, trial)) != null) {

                        
                        if (!history.contains(trial)) {

                            String newSol = sol + dirLabels[i][1];

                            if (isSolved(trial))
                                return newSol;

                            open.add(new Board(trial, newSol, x + dx, y + dy));
                            history.add(trial);
                        }
                    }

                
                } else if ((trial = move(x, y, dx, dy, trial)) != null) {

                    if (!history.contains(trial)) {
                        String newSol = sol + dirLabels[i][0];
                        open.add(new Board(trial, newSol, x + dx, y + dy));
                        history.add(trial);
                    }
                }
            }
        }
        return "No solution";
    }

    public static void main(String[] a) {
        String level = "#######,#     #,#     #,#. #  #,#. $$ #,"
                + "#.$$  #,#.#  @#,#######";
        System.out.println(new Sokoban(level.split(",")).solve());
    }
}