187 lines
		
	
	
	
		
			5 KiB
		
	
	
	
		
			Java
		
	
	
	
	
	
			
		
		
	
	
			187 lines
		
	
	
	
		
			5 KiB
		
	
	
	
		
			Java
		
	
	
	
	
	
| package net.minecraft.util.random;
 | |
| 
 | |
| import com.google.common.collect.ImmutableList;
 | |
| import com.google.common.collect.Lists;
 | |
| import com.mojang.serialization.Codec;
 | |
| import com.mojang.serialization.MapCodec;
 | |
| import java.util.Arrays;
 | |
| import java.util.List;
 | |
| import java.util.Objects;
 | |
| import java.util.Optional;
 | |
| import java.util.function.Function;
 | |
| import net.minecraft.util.ExtraCodecs;
 | |
| import net.minecraft.util.RandomSource;
 | |
| import org.jetbrains.annotations.Nullable;
 | |
| 
 | |
| public final class WeightedList<E> {
 | |
| 	private static final int FLAT_THRESHOLD = 64;
 | |
| 	private final int totalWeight;
 | |
| 	private final List<Weighted<E>> items;
 | |
| 	@Nullable
 | |
| 	private final WeightedList.Selector<E> selector;
 | |
| 
 | |
| 	WeightedList(List<? extends Weighted<E>> items) {
 | |
| 		this.items = List.copyOf(items);
 | |
| 		this.totalWeight = WeightedRandom.getTotalWeight(items, Weighted::weight);
 | |
| 		if (this.totalWeight == 0) {
 | |
| 			this.selector = null;
 | |
| 		} else if (this.totalWeight < 64) {
 | |
| 			this.selector = new WeightedList.Flat<>(this.items, this.totalWeight);
 | |
| 		} else {
 | |
| 			this.selector = new WeightedList.Compact<>(this.items);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	public static <E> WeightedList<E> of() {
 | |
| 		return new WeightedList<>(List.of());
 | |
| 	}
 | |
| 
 | |
| 	public static <E> WeightedList<E> of(E element) {
 | |
| 		return new WeightedList<>(List.of(new Weighted<>(element, 1)));
 | |
| 	}
 | |
| 
 | |
| 	@SafeVarargs
 | |
| 	public static <E> WeightedList<E> of(Weighted<E>... items) {
 | |
| 		return new WeightedList<>(List.of(items));
 | |
| 	}
 | |
| 
 | |
| 	public static <E> WeightedList<E> of(List<Weighted<E>> items) {
 | |
| 		return new WeightedList<>(items);
 | |
| 	}
 | |
| 
 | |
| 	public static <E> WeightedList.Builder<E> builder() {
 | |
| 		return new WeightedList.Builder<>();
 | |
| 	}
 | |
| 
 | |
| 	public boolean isEmpty() {
 | |
| 		return this.items.isEmpty();
 | |
| 	}
 | |
| 
 | |
| 	public <T> WeightedList<T> map(Function<E, T> mapper) {
 | |
| 		return new WeightedList(Lists.transform(this.items, weighted -> weighted.map((Function<T, E>)mapper)));
 | |
| 	}
 | |
| 
 | |
| 	public Optional<E> getRandom(RandomSource random) {
 | |
| 		if (this.selector == null) {
 | |
| 			return Optional.empty();
 | |
| 		} else {
 | |
| 			int i = random.nextInt(this.totalWeight);
 | |
| 			return Optional.of(this.selector.get(i));
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	public E getRandomOrThrow(RandomSource random) {
 | |
| 		if (this.selector == null) {
 | |
| 			throw new IllegalStateException("Weighted list has no elements");
 | |
| 		} else {
 | |
| 			int i = random.nextInt(this.totalWeight);
 | |
| 			return this.selector.get(i);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	public List<Weighted<E>> unwrap() {
 | |
| 		return this.items;
 | |
| 	}
 | |
| 
 | |
| 	public static <E> Codec<WeightedList<E>> codec(Codec<E> elementCodec) {
 | |
| 		return Weighted.codec(elementCodec).listOf().xmap(WeightedList::of, WeightedList::unwrap);
 | |
| 	}
 | |
| 
 | |
| 	public static <E> Codec<WeightedList<E>> codec(MapCodec<E> elementCodec) {
 | |
| 		return Weighted.codec(elementCodec).listOf().xmap(WeightedList::of, WeightedList::unwrap);
 | |
| 	}
 | |
| 
 | |
| 	public static <E> Codec<WeightedList<E>> nonEmptyCodec(Codec<E> elementCodec) {
 | |
| 		return ExtraCodecs.nonEmptyList(Weighted.codec(elementCodec).listOf()).xmap(WeightedList::of, WeightedList::unwrap);
 | |
| 	}
 | |
| 
 | |
| 	public static <E> Codec<WeightedList<E>> nonEmptyCodec(MapCodec<E> elementCodec) {
 | |
| 		return ExtraCodecs.nonEmptyList(Weighted.codec(elementCodec).listOf()).xmap(WeightedList::of, WeightedList::unwrap);
 | |
| 	}
 | |
| 
 | |
| 	public boolean contains(E element) {
 | |
| 		for (Weighted<E> weighted : this.items) {
 | |
| 			if (weighted.value().equals(element)) {
 | |
| 				return true;
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		return false;
 | |
| 	}
 | |
| 
 | |
| 	public boolean equals(@Nullable Object object) {
 | |
| 		if (this == object) {
 | |
| 			return true;
 | |
| 		} else {
 | |
| 			return !(object instanceof WeightedList<?> weightedList)
 | |
| 				? false
 | |
| 				: this.totalWeight == weightedList.totalWeight && Objects.equals(this.items, weightedList.items);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	public int hashCode() {
 | |
| 		int i = this.totalWeight;
 | |
| 		return 31 * i + this.items.hashCode();
 | |
| 	}
 | |
| 
 | |
| 	public static class Builder<E> {
 | |
| 		private final ImmutableList.Builder<Weighted<E>> result = ImmutableList.builder();
 | |
| 
 | |
| 		public WeightedList.Builder<E> add(E element) {
 | |
| 			return this.add(element, 1);
 | |
| 		}
 | |
| 
 | |
| 		public WeightedList.Builder<E> add(E element, int weight) {
 | |
| 			this.result.add(new Weighted<>(element, weight));
 | |
| 			return this;
 | |
| 		}
 | |
| 
 | |
| 		public WeightedList<E> build() {
 | |
| 			return new WeightedList<>(this.result.build());
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	static class Compact<E> implements WeightedList.Selector<E> {
 | |
| 		private final Weighted<?>[] entries;
 | |
| 
 | |
| 		Compact(List<Weighted<E>> entries) {
 | |
| 			this.entries = (Weighted<?>[])entries.toArray(Weighted[]::new);
 | |
| 		}
 | |
| 
 | |
| 		@Override
 | |
| 		public E get(int index) {
 | |
| 			for (Weighted<?> weighted : this.entries) {
 | |
| 				index -= weighted.weight();
 | |
| 				if (index < 0) {
 | |
| 					return (E)weighted.value();
 | |
| 				}
 | |
| 			}
 | |
| 
 | |
| 			throw new IllegalStateException(index + " exceeded total weight");
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	static class Flat<E> implements WeightedList.Selector<E> {
 | |
| 		private final Object[] entries;
 | |
| 
 | |
| 		Flat(List<Weighted<E>> entries, int size) {
 | |
| 			this.entries = new Object[size];
 | |
| 			int i = 0;
 | |
| 
 | |
| 			for (Weighted<E> weighted : entries) {
 | |
| 				int j = weighted.weight();
 | |
| 				Arrays.fill(this.entries, i, i + j, weighted.value());
 | |
| 				i += j;
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		@Override
 | |
| 		public E get(int index) {
 | |
| 			return (E)this.entries[index];
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	interface Selector<E> {
 | |
| 		E get(int index);
 | |
| 	}
 | |
| }
 |