RoboScript #4 - RS3 Patterns to the Rescue

文章描述了一个关于RoboScript的编程问题,这是一种控制机器人行动的语言。挑战涉及解析包括模式定义(p和P命令)在内的复杂指令序列,并处理可能的嵌套和递归。解决方案包括创建Sentence类来存储和执行指令,使用HashSet记录机器人的位置,以及检测和防止模式的无限递归。最终代码实现了指令的编译和执行,以及绘制机器人路径的功能。
摘要由CSDN通过智能技术生成

RoboScript #4 - RS3 Patterns to the Rescue | Codewarshttps://www.codewars.com/kata/594b898169c1d644f900002e/java

题目概括:

背景是开发一个操纵机器人行动的脚本语言RoboScript, 实现以下语言标准
 

  • F - Move forward by 1 step in the direction that it is currently pointing. Initially, the robot faces to the right.
  • L - Turn "left" (i.e. rotate 90 degrees anticlockwise)
  • R - Turn "right" (i.e. rotate 90 degrees clockwise)
  • Fn - Execute the F command n times (NOTE: n may be more than 1 digit long!)
  • Ln - Execute L n times
  • Rn - Execute R n times
  • (SEQUENCE_OF_COMMANDS)n ... is equivalent to ...SEQUENCE_OF_COMMANDS...SEQUENCE_OF_COMMANDS (repeatedly executed "n" times)
  • p(n)<CODE_HERE>q 
    WHERE:
    
    • p is a "keyword" that declares the beginning of a pattern definition (much like the function keyword in JavaScript or the def keyword in Python)
    • (n) is any non-negative integer (without the round brackets) which acts as a unique identifier for the pattern (much like a function/method name)
    • <CODE_HERE> is any valid RoboScript code (without the angled brackets)
    • q is a "keyword" that marks the end of a pattern definition (like the end keyword in Ruby)

难点:

1、嵌套括号的处理,例如p312(F2LF2R)2qP312((F2R3)3)

2、题目中的要实现类似函数功能的`pattern`,这类似于函数,可在指令序列的任意地方声明和调用,极大增加了代码结构的复杂性

3、解析指令序列后,要求输出Robot的行动轨迹,需要一点技巧

4、OOP编程的抽象能力

思路:

1、解释:采集字符串的数据格式化为指令类,但把p指令单独看待,将p声明的pattern放入Map<Integer, Sentence> patternPool中,以便P指令调用

2、执行:定义Sentence类,储存语句类型(FLR,或者FLR组成的序列),执行次数n。定义execute方法调用n次相应的指令类的execute方法。
2、画图:定义Position类, 将Robot走过的position放入HashSet中,同时记录Robot走过的上下左右边界。有了走过的坐标和走过的区域边界就能模拟出Robot的活动路径了。
边界值用两层检查坐标有没有在HashSet内, 有则StringBuider append “*”,没有则append “空格”。

提交时发现的错误以及处理方式:

1、爆内存

傻傻的定义了巨无霸二维数组来模拟走过的路径,后改用HashSet只记录已走过的点坐标得到解决

1、StackOverflowError

好的家伙这12个样例没有过,点开错误一看,奥原来是StackOverflowError,痛骂样例嵌套了了几千层括号祸害我
再仔细一看,奥原来是无限套娃调用要抛出异常 :)

故在P类中添加该checkAndPreventLoop方法检查是否有套娃现象。解决!

    private static class P implements Instruction {
        @Override
        public void execute(int args) {
            if (!patternPool.containsKey(args))
                throw new RuntimeException("Unknown pattern: " + args);

            checkAndPreventLoop(args, new HashSet<>());

            patternPool.get(args).execute();
        }

        private void checkAndPreventLoop(int patternId, Set<Integer> visitedPatterns) {
            if (visitedPatterns.contains(patternId)) {
                throw new RuntimeException("Pattern recursion loop detected: " + patternId);
            }

            visitedPatterns.add(patternId);

            Sentence pattern = patternPool.get(patternId);
            for (Sentence s : pattern.nestedSentences) {
                if (s.instructionType == InstructionType.P) {
                    checkAndPreventLoop(s.arg, new HashSet<>(visitedPatterns));
                }
            }
        }
    }

绿油油的很好!下一个!

 最终代码:

import java.util.*;

public class RoboScript3 {
    private static final Map<InstructionType, Instruction> instructionMap = new EnumMap<>(InstructionType.class);
    private static final Map<Integer, Sentence> patternPool = new HashMap<>();
    private static Direction movingDirection = Direction.E;
    private static final Set<Position> visitedPositions = new HashSet<>();
    private static final int[] position = {0, 0};
    private static int minX = 0, minY = 0, maxX = 0, maxY = 0;
    enum Direction {S, W, N, E};
    enum InstructionType {F, L, R, P}

    static {
        instructionMap.put(InstructionType.F, new F());
        instructionMap.put(InstructionType.L, new L());
        instructionMap.put(InstructionType.R, new R());
        instructionMap.put(InstructionType.P, new P());
    }

    public static String execute(String code) {
        movingDirection = Direction.E;
        visitedPositions.clear();
        patternPool.clear();
        position[0] = position[1] = 0;
        minX = minY = 0;
        maxX = maxY = 0;
        visitedPositions.add(new Position(0, 0));

        List<Sentence> program = compile(code);
        for (Sentence s : program) {
            s.execute();
        }

        return drawRoutine();
    }

    private static List<Sentence> compile(String code) {
        List<Sentence> sentenceList = new ArrayList<>();
        int i = 0;
        while (i < code.length()) {
            if (code.charAt(i) == '(') {
                int j = i;
                int bracketCounter = 0;
                while (j < code.length()) {
                    if (code.charAt(j) == '(') {
                        bracketCounter++;
                    } else if (code.charAt(j) == ')') {
                        bracketCounter--;
                        if (bracketCounter == 0) {
                            break;
                        }
                    }
                    j++;
                }
                int k = j + 1;
                while (k < code.length() && Character.isDigit(code.charAt(k))) {
                    k++;
                }
                int repeat = k > j + 1 ? Integer.parseInt(code.substring(j + 1, k)) : 1;
                sentenceList.add(new Sentence(compile(code.substring(i + 1, j)), repeat));
                i = k;
            } else if (code.charAt(i) == 'p') { //pn<sentence>q
                // Declare pattern
                int j = i + 1; // skip 'p'
                while (j < code.length() && Character.isDigit(code.charAt(j))) {
                    j++;
                }
//                if (j <= i + 1 || j + 1 >= code.length())
//                    throw new RuntimeException("Syntax Error: pn<sentence>q");
                int patternId = Integer.parseInt(code.substring(i + 1, j));

                // Find q position
                int q = j;
                while (q < code.length() && code.charAt(q) != 'q') {
                    q++;
                }
                if (patternPool.containsKey(patternId))
                    throw new RuntimeException("The pattern " + patternId + " has been defined before");
                patternPool.put(patternId, new Sentence(compile(code.substring(j, q)), 1));
                i = q + 1;
            } else {
                int j = i + 1;
                while (j < code.length() && Character.isDigit(code.charAt(j))) {
                    j++;
                }
                int repeat = j > i + 1 ? Integer.parseInt(code.substring(i + 1, j)) : 1;
                sentenceList.add(new Sentence(InstructionType.valueOf(String.valueOf(code.charAt(i))), repeat));
                i = j;
            }
        }
        return sentenceList;
    }

    private static String drawRoutine() {
        StringBuilder builder = new StringBuilder((maxY - minY + 1) * (maxX - minX + 2));
        for (int i = minX; i <= maxX; i++) {
            for (int j = minY; j <= maxY; j++) {
                if (visitedPositions.contains(new Position(i, j))) {
                    builder.append('*');
                } else {
                    builder.append(' ');
                }
            }
            builder.append("\r\n");
        }
        return builder.toString().replaceAll("\r\n$", "");
    }
    private static class Sentence {
        private final InstructionType instructionType;
        private final List<Sentence> nestedSentences;
        private final int arg;

        public Sentence(InstructionType instructionType, int arg) {
            this.instructionType = instructionType;
            this.nestedSentences = null;
            this.arg = arg;
        }

        public Sentence(List<Sentence> nestedSentences, int arg) {
            this.instructionType = null;
            this.nestedSentences = nestedSentences;
            this.arg = arg;
        }

        public void execute() {
            if (instructionType != null) {
                Instruction instruction = instructionMap.get(instructionType);
                instruction.execute(arg);
            } else {
                for (int i = 0; i < arg; i++) {
                    for (Sentence sentence : nestedSentences) {
                        sentence.execute();
                    }
                }
            }
        }
    }

    interface Instruction {
        void execute(int args);
    }

    private static class F implements Instruction {
        @Override
        public void execute(int args) {
            for (int i = 0; i < args; i++) {
                switch (movingDirection) {
                    case E:
                        position[1]++;
                        break;
                    case N:
                        position[0]--;
                        break;
                    case S:
                        position[0]++;
                        break;
                    case W:
                        position[1]--;
                }
                visitedPositions.add(new Position(position[0], position[1]));
                maxX = Math.max(maxX, position[0]);
                minX = Math.min(minX, position[0]);
                maxY = Math.max(maxY, position[1]);
                minY = Math.min(minY, position[1]);
            }
        }
    }

    private static class R implements Instruction {
        @Override
        public void execute(int args) {
            Direction[] directions = Direction.values();
            int newIndex = (movingDirection.ordinal() + args) % 4;
            movingDirection = directions[newIndex];
        }
    }

    private static class L implements Instruction {
        @Override
        public void execute(int args) {
            Direction[] directions = Direction.values();
            int newIndex = Math.floorMod(movingDirection.ordinal() - args, 4);
            movingDirection = directions[newIndex];
        }
    }

    private static class P implements Instruction {
        @Override
        public void execute(int args) {
            if (!patternPool.containsKey(args))
                throw new RuntimeException("Unknown pattern: " + args);

            checkAndPreventLoop(args, new HashSet<>());

            patternPool.get(args).execute();
        }

        private void checkAndPreventLoop(int patternId, Set<Integer> visitedPatterns) {
            if (visitedPatterns.contains(patternId)) {
                throw new RuntimeException("Pattern recursion loop detected: " + patternId);
            }

            visitedPatterns.add(patternId);

            Sentence pattern = patternPool.get(patternId);
            for (Sentence s : pattern.nestedSentences) {
                if (s.instructionType == InstructionType.P) {
                    checkAndPreventLoop(s.arg, new HashSet<>(visitedPatterns));
                }
            }
        }
    }


//    private static class P implements Instruction {
//        @Override
//        public void execute(int args) {
//            if (!patternPool.containsKey(args))
//                throw new RuntimeException("Unknown pattern: " + args);
//
//            patternPool.get(args).execute();
//        }
//    }

    private static class Position {
        private final int x;
        private final int y;

        public Position(int x, int y) {
            this.x = x;
            this.y = y;
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
            Position position = (Position) o;
            return x == position.x && y == position.y;
        }

        @Override
        public int hashCode() {
            return Objects.hash(x, y);
        }
    }
}

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值