001/*
002 * This file is part of Baritone.
003 *
004 * Baritone is free software: you can redistribute it and/or modify
005 * it under the terms of the GNU Lesser General Public License as published by
006 * the Free Software Foundation, either version 3 of the License, or
007 * (at your option) any later version.
008 *
009 * Baritone is distributed in the hope that it will be useful,
010 * but WITHOUT ANY WARRANTY; without even the implied warranty of
011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
012 * GNU Lesser General Public License for more details.
013 *
014 * You should have received a copy of the GNU Lesser General Public License
015 * along with Baritone.  If not, see <https://www.gnu.org/licenses/>.
016 */
017
018package baritone.api.pathing.calc;
019
020import baritone.api.Settings;
021import baritone.api.pathing.goals.Goal;
022import baritone.api.pathing.movement.IMovement;
023import baritone.api.utils.BetterBlockPos;
024
025import java.util.HashSet;
026import java.util.List;
027
028/**
029 * @author leijurv, Brady
030 */
031public interface IPath {
032
033    /**
034     * Ordered list of movements to carry out.
035     * movements.get(i).getSrc() should equal positions.get(i)
036     * movements.get(i).getDest() should equal positions.get(i+1)
037     * movements.size() should equal positions.size()-1
038     *
039     * @return All of the movements to carry out
040     */
041    List<IMovement> movements();
042
043    /**
044     * All positions along the way.
045     * Should begin with the same as getSrc and end with the same as getDest
046     *
047     * @return All of the positions along this path
048     */
049    List<BetterBlockPos> positions();
050
051    /**
052     * This path is actually going to be executed in the world. Do whatever additional processing is required.
053     * (as opposed to Path objects that are just constructed every frame for rendering)
054     *
055     * @return The result of path post processing
056     */
057    default IPath postProcess() {
058        throw new UnsupportedOperationException();
059    }
060
061    /**
062     * Returns the number of positions in this path. Equivalent to {@code positions().size()}.
063     *
064     * @return Number of positions in this path
065     */
066    default int length() {
067        return positions().size();
068    }
069
070    /**
071     * @return The goal that this path was calculated towards
072     */
073    Goal getGoal();
074
075    /**
076     * Returns the number of nodes that were considered during calculation before
077     * this path was found.
078     *
079     * @return The number of nodes that were considered before finding this path
080     */
081    int getNumNodesConsidered();
082
083    /**
084     * Returns the start position of this path. This is the first element in the
085     * {@link List} that is returned by {@link IPath#positions()}.
086     *
087     * @return The start position of this path
088     */
089    default BetterBlockPos getSrc() {
090        return positions().get(0);
091    }
092
093    /**
094     * Returns the end position of this path. This is the last element in the
095     * {@link List} that is returned by {@link IPath#positions()}.
096     *
097     * @return The end position of this path.
098     */
099    default BetterBlockPos getDest() {
100        List<BetterBlockPos> pos = positions();
101        return pos.get(pos.size() - 1);
102    }
103
104    /**
105     * Returns the estimated number of ticks to complete the path from the given node index.
106     *
107     * @param pathPosition The index of the node we're calculating from
108     * @return The estimated number of ticks remaining frm the given position
109     */
110    default double ticksRemainingFrom(int pathPosition) {
111        double sum = 0;
112        //this is fast because we aren't requesting recalculation, it's just cached
113        List<IMovement> movements = movements();
114        for (int i = pathPosition; i < movements.size(); i++) {
115            sum += movements.get(i).getCost();
116        }
117        return sum;
118    }
119
120    /**
121     * Cuts off this path at the loaded chunk border, and returns the resulting path. Default
122     * implementation just returns this path, without the intended functionality.
123     * <p>
124     * The argument is supposed to be a BlockStateInterface LOL LOL LOL LOL LOL
125     *
126     * @param bsi The block state lookup, highly cursed
127     * @return The result of this cut-off operation
128     */
129    default IPath cutoffAtLoadedChunks(Object bsi) {
130        throw new UnsupportedOperationException();
131    }
132
133    /**
134     * Cuts off this path using the min length and cutoff factor settings, and returns the resulting path.
135     * Default implementation just returns this path, without the intended functionality.
136     *
137     * @param destination The end goal of this path
138     * @return The result of this cut-off operation
139     * @see Settings#pathCutoffMinimumLength
140     * @see Settings#pathCutoffFactor
141     */
142    default IPath staticCutoff(Goal destination) {
143        throw new UnsupportedOperationException();
144    }
145
146
147    /**
148     * Performs a series of checks to ensure that the assembly of the path went as expected.
149     */
150    default void sanityCheck() {
151        List<BetterBlockPos> path = positions();
152        List<IMovement> movements = movements();
153        if (!getSrc().equals(path.get(0))) {
154            throw new IllegalStateException("Start node does not equal first path element");
155        }
156        if (!getDest().equals(path.get(path.size() - 1))) {
157            throw new IllegalStateException("End node does not equal last path element");
158        }
159        if (path.size() != movements.size() + 1) {
160            throw new IllegalStateException("Size of path array is unexpected");
161        }
162        HashSet<BetterBlockPos> seenSoFar = new HashSet<>();
163        for (int i = 0; i < path.size() - 1; i++) {
164            BetterBlockPos src = path.get(i);
165            BetterBlockPos dest = path.get(i + 1);
166            IMovement movement = movements.get(i);
167            if (!src.equals(movement.getSrc())) {
168                throw new IllegalStateException("Path source is not equal to the movement source");
169            }
170            if (!dest.equals(movement.getDest())) {
171                throw new IllegalStateException("Path destination is not equal to the movement destination");
172            }
173            if (seenSoFar.contains(src)) {
174                throw new IllegalStateException("Path doubles back on itself, making a loop");
175            }
176            seenSoFar.add(src);
177        }
178    }
179}