def solve_last(self):
#        log.info("solving last:\n{}",format(self.p))
        """Löst die letzten beiden Elemente, womit dann die ganze Reihe gelöst ist"""
        r, d, l, u = (0, 1), (1, 0), (0, -1), (-1, 0)
        final_lst_pos = puz.get_position(self.s, self.s[0][-1])
        lst_pos = self.lst_pos()
        # 1. Fall: 4 ist über der 3 und beide werden zusammen 'reinrotiert' -> fertig
        if all(lst_pos == np.array(final_lst_pos) + d):
            pa = PosAction((0, len(self.s[0]) - 2), [r, d])
            locked = self.locked.union({self.lst_pos()})
            self.exec_pa(pa, locked)
        # 2. Fall: 3 und 4 sind vertauscht -> Überführung in 3. Fall
        elif all(lst_pos == np.array(final_lst_pos) + l):
            pa = PosAction((1, len(self.s[0]) - 2), [u])
            self.exec_pa(pa, self.locked)
            self.solve_last()
        # 3. Fall: Überführung in 1. Fall
        elif (all(lst_pos == np.array(final_lst_pos) + (1, -1)) and
              all(puz.empty_position(self.p) == np.array(final_lst_pos) + l)):
            pa = PosAction(puz.empty_position(self.p), [r, d, d, l, u, u, r, d, l,
                                                        u, r, d, d, l, u, r, d])
            self.exec_pa(pa, set())
            self.solve_last()
        # 4. Fall (default): Die 4 ist irgendwo anders -> Überführung in 1.
        else:
            self.p, acts = move_one_tile(self.p, self.s[0][-1],
                                         tuple(np.array(final_lst_pos) + d), self.locked)
            self._moves += acts
            self.solve_last()
    def execute(self, p, locked):
        """p: das Feld auf dem die Aktion ausgeführt werden soll
        locked: eine Menge von Positionen die nicht bewegt werden dürfen. 
        Die erste Position die durch die Aktion betreten wird, wird autoamtisch gelockt"""
        log.debug("executing PosAction; start: {}, actions: {}".format(self.start_position,
                                                                       self.actions))
        l = locked.copy()
        l.add(self.locked_position()) # die erste Position wird automatisch gelockt!
        path = a_star(p.shape[0],
                      puz.empty_position(p),
                      self.start_position, l)
        assert path, "PosAction not executable, starting position not reachable"
        init_actions = coords_to_actions(path)
#        log.info("moving into start pos., path: {}".format(init_actions))
        p = puz.apply_actions(p, init_actions)
        p = puz.apply_actions(p, self.actions)
        return p, init_actions + self.actions