Beispiel #1
0
 def gen_diff(cls, pattern):
     # Generate a diff string for each direction and store it
     SOLVED_CUBE_STR = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz01"
     cube = Cube(SOLVED_CUBE_STR)
     s1 = cube.flat_str()
     cube.sequence(pattern)
     s2 = cube.flat_str()
     return [s1.index(s2[i]) for i in range(len(s1))]
Beispiel #2
0
    def test(self):
        if not (self.fwd and self.rev):
            return False

        c = Cube(self.initial)
        c.sequence(self.fwd)
        c.sequence(self.rev)
        return (c.flat_str() == self.initial)
Beispiel #3
0
def data_generator():
    c = Cube(SOLVED_STATE)
    print_cube(c)
    for x in np.random.randint(0, 12, 100):
        getattr(c, actions[x])()  #scramble cube
        print(f"action={actions[x]}")
        print_cube(c)
        str = c.flat_str()
        print("12 children")
        for a in actions:
            c_working = Cube(str)
            getattr(c_working, a)()
            print_cube(c_working)
        print("--------------------")
Beispiel #4
0
def main():
    global log
    # Parse arguments and initialize logging
    parser = ArgumentParser(
        __name__, "Find permutations of PLL algorithms " +
        "that return the cube to a solved state. If " +
        "neither '--anki' or '--trainer' is specified, " +
        "print all identified sequences and their " +
        "patterns' respective steps.")
    parser.add_argument(action="store",
                        nargs="*",
                        dest="algorithms",
                        help="Override the list of algorithms that will be " +
                        "tested. If not specified, use all algorithms found " +
                        " in ./pll.json")
    parser.add_argument("-d",
                        "--max-depth",
                        default=4,
                        help="Set maximum " + "recursion depth (default: 4)")
    parser.add_argument("-a",
                        "--anki",
                        action="store_true",
                        default=False,
                        help="Print all identified patterns in Anki's flash " +
                        "card format")
    parser.add_argument("-s",
                        "--search",
                        action="store",
                        nargs="+",
                        help="Only include sequences that use at least one " +
                        "of the specified patterns")
    parser.add_argument("-t",
                        "--trainer",
                        action="store_true",
                        default=False,
                        help="Pick a random sequence and present the steps " +
                        "one by one. Calculate time to solve each step, as " +
                        "well as total time and average time per step. " +
                        "Advance from one step to the next by pressing any " +
                        "key")
    parser.add_argument("-v",
                        nargs="?",
                        action=VAction,
                        dest="verbosity",
                        help="Set log level. Accepted up to 3 times (e.g. " +
                        "-vvv)")
    args = parser.parse_args()
    log = init_logging(args)

    # Read available algorithms from './pll.json' and parse them into Sequence
    # objects.
    #
    # TODO: Make the file path user-controllable
    pll = {}
    with open('./pll.json') as f:
        _pll = json.load(f)
        for j in _pll["algorithms"]:
            pll[j["name"]] = Sequence(j["name"], j["fwd"], j["rev"], j["fmt"])
            if "variants" in j:
                for variant in j["variants"]:
                    v_fwd = variant
                    if len(v_fwd) == 1:
                        v_rev = f"{v_fwd}i"
                        v_fmt = v_fwd
                    elif len(v_fwd) == 2 and v_fwd[1] == "i":
                        v_rev = v_fwd[:-1]
                        v_fmt = f"{v_rev}'"
                    else:
                        continue

                    v = (v_fwd, v_rev, v_fmt)

                    pll[f"{v[2]}+{j['name']}"] = Sequence(
                        f"{v[2]}+{j['name']}", f"{v[0]} {j['fwd']}",
                        f"{j['rev']} {v[1]}", f"{v[2]} {j['fmt']}")
            log.debug(f"Parsed sequence {j['name']:<2}: {j['fmt']}")


## TODO:
#       "Na": "Z D Ri U R R Di R D Ui Ri U R R Di R Ui R",
#       "Nb": "Z Ui R Di R R U Ri D Ui R Di R R U Ri D Ri",
#       "Gc": "Ri Ri Ui E R Ui R U Ri U Ei R R Fi S Ri Si F",
#       "V":  "Ri U Ri Di Ei Ri Fi R R Ui Ri U Ri F R F",

# Test all sequences
    for name, seq in pll.items():
        if not seq.test():
            log.error(f"Sequence {name} did not return cube to solved state")

    # Create a cube object and place it in the solved state
    cube = Cube(Sequence.initial)

    if args.algorithms:
        # If a list of algorithms was provided, drop any invalid entries
        sequences = list(set(args.algorithms).intersection(set(pll.keys())))
    else:
        # Otherwise, use the full set
        sequences = list(pll.keys())
    starting_sequences = set(sequences)

    limit_one = args.trainer
    patterns = find_pattern(cube.flat_str(),
                            pll,
                            sequences,
                            int(args.max_depth),
                            limit_one=limit_one)
    used_sequences = set()
    for p in patterns:
        pattern = p.split()
        # If a whitelist was provided, use it to filter the pattern list
        if (not args.search) or len(
                set(args.search).intersection(set(pattern))) > 0:
            if args.anki:
                # Print the raw list of matched sequences in Anki's flashcard
                # format ([front],[back],[tags])
                out = f"{' -> '.join(pattern[:-1])} -> _,"
                tags = []
                # Print each pattern wrapped in a <div>
                for a in pattern:
                    out = f"{out}<div>{str(pll[a])}</div>"
                    tags.append(a)
                out = f"{out},{' '.join(tags)}"
                print(out)
            elif not args.trainer:
                # Default case (not Anki or trainer mode): Print each sequence,
                # including both the list of patterns and their respective
                # steps.
                print(f"{' -> '.join(pattern)}")
                for a in pattern:
                    print(f"{a:<2} - {str(pll[a])}")
                print()
            used_sequences = used_sequences.union(set(pattern))

    # Print a list of all sequences that were not used at least once
    not_used = starting_sequences - used_sequences
    if len(not_used):
        log.debug(f"Not used: {', '.join(list(not_used))}")

    ##
    #  Whenever a key is pressed, add it to the queue to be handled inside the
    #  timer loop.
    ##
    def read_input(_queue):
        # Source: https://stackoverflow.com/questions/510357/21659588#21659588
        fd = sys.stdin.fileno()
        old_settings = termios.tcgetattr(fd)
        try:
            tty.setraw(sys.stdin.fileno())
            ch = sys.stdin.read(1)
            # Handle arrow keys, which consist of 3-character ANSI escape
            # sequences. Source: https://stackoverflow.com/questions/22397289
            if ord(ch) == 27:
                ch = sys.stdin.read(2)
                _queue.put(' ')
            else:
                _queue.put(ch)
        finally:
            termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)

    ##
    #  Create a separate thread to listen for (blocking) keyboard input events
    ##
    def spawn_thread(_queue):
        thread = threading.Thread(target=read_input, args=(_queue, ))
        thread.start()

    # 'Trainer' mode: select a random sequence from the identified list and step
    # through it. Each step is timed; advance steps by pressing any key. Press
    # ESC, Ctrl+C, or Ctrl+D to terminate, or advance through the last step.
    if args.trainer:
        if not len(patterns):
            log.critical("No patterns found; cannot continue")
            return 1

        pattern = random.choice(list(patterns)).split(" ")
        interval = 0.01
        elapsed = 0
        spacer = ""
        out = ""

        _queue = queue.Queue()
        spawn_thread(_queue)

        if len(pattern):
            last = False
            cont = True
            total = 0
            count = len(pattern)
            out = f"\x1bc{pattern.pop(0)}"

            while cont:
                try:
                    val = _queue.get(False)
                    if val:
                        if ord(val) in [3, 4, 27]:
                            total += elapsed
                            print(f"\nTotal: {total:.2f}s")
                            sys.exit(0)
                        try:
                            total += elapsed
                            out = f"{out} [{elapsed:.2f}s]"
                            # If the next step is the last one, mask it while
                            # the timer is running.
                            if len(pattern) == 1:
                                last_pattern = pattern.pop(0)
                                out = f"{out} -> _"
                            else:
                                out = f"{out} -> {pattern.pop(0)}"
                            elapsed = 0
                        except IndexError:
                            pass

                        if last:
                            cont = False
                        else:
                            # Spawn a new listener thread if there are more
                            # queued steps.
                            spawn_thread(_queue)

                        if not len(pattern):
                            last = True
                except queue.Empty:
                    pass

                sys.stdout.write("\r")
                sys.stdout.write(f"{out} [{elapsed:.2f}s]")
                sys.stdout.flush()
                time.sleep(interval)
                elapsed += interval

            sys.stdout.write("\r")
            sys.stdout.flush()
            print(out.replace(" -> _", f" -> {last_pattern}"))
            print(f"Total: {total:.2f}s")
            print(f"Avg:   {total/count:.2f}s")