diff options
10 files changed, 668 insertions, 1 deletions
diff --git a/src/main/java/at/hannibal2/skyhanni/SkyHanniMod.kt b/src/main/java/at/hannibal2/skyhanni/SkyHanniMod.kt index ce729c24d..bb1e05cfc 100644 --- a/src/main/java/at/hannibal2/skyhanni/SkyHanniMod.kt +++ b/src/main/java/at/hannibal2/skyhanni/SkyHanniMod.kt @@ -268,6 +268,7 @@ import at.hannibal2.skyhanni.features.mining.crystalhollows.CrystalHollowsNamesI import at.hannibal2.skyhanni.features.mining.crystalhollows.CrystalHollowsWalls import at.hannibal2.skyhanni.features.mining.eventtracker.MiningEventDisplay import at.hannibal2.skyhanni.features.mining.eventtracker.MiningEventTracker +import at.hannibal2.skyhanni.features.mining.fossilexcavator.FossilExcavator import at.hannibal2.skyhanni.features.mining.powdertracker.PowderTracker import at.hannibal2.skyhanni.features.minion.InfernoMinionFeatures import at.hannibal2.skyhanni.features.minion.MinionCollectLogic @@ -658,6 +659,7 @@ class SkyHanniMod { loadModule(GardenCropMilestoneDisplay) loadModule(GardenCustomKeybinds) loadModule(ChickenHeadTimer()) + loadModule(FossilExcavator) loadModule(GardenOptimalSpeed()) loadModule(GardenLevelDisplay()) loadModule(FarmingWeightDisplay()) diff --git a/src/main/java/at/hannibal2/skyhanni/config/features/mining/FossilExcavatorConfig.java b/src/main/java/at/hannibal2/skyhanni/config/features/mining/FossilExcavatorConfig.java new file mode 100644 index 000000000..d9a3ac7ba --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/config/features/mining/FossilExcavatorConfig.java @@ -0,0 +1,29 @@ +package at.hannibal2.skyhanni.config.features.mining; + +import at.hannibal2.skyhanni.config.FeatureToggle; +import at.hannibal2.skyhanni.config.core.config.Position; +import com.google.gson.annotations.Expose; +import io.github.notenoughupdates.moulconfig.annotations.ConfigEditorBoolean; +import io.github.notenoughupdates.moulconfig.annotations.ConfigLink; +import io.github.notenoughupdates.moulconfig.annotations.ConfigOption; + +public class FossilExcavatorConfig { + + @Expose + @ConfigOption(name = "Fossil Excavator Helper", desc = "Helps you find fossils in the fossil excavator. " + + "§eWill always solve if you have at least 18 clicks. Solves everything except Spine, Ugly and Helix in 16 clicks.") + @ConfigEditorBoolean + @FeatureToggle + public boolean enabled = true; + + @Expose + @ConfigOption(name = "Show Percentage", desc = "Shows percentage chance that next click will be a fossil. " + + "§eThis assumes there is a fossil hidden in the dirt.") + @ConfigEditorBoolean + public boolean showPercentage = true; + + @Expose + @ConfigLink(owner = FossilExcavatorConfig.class, field = "enabled") + public Position position = new Position(183, 212, false, true); + +} diff --git a/src/main/java/at/hannibal2/skyhanni/config/features/mining/MiningConfig.java b/src/main/java/at/hannibal2/skyhanni/config/features/mining/MiningConfig.java index 2741955dc..5beafceb0 100644 --- a/src/main/java/at/hannibal2/skyhanni/config/features/mining/MiningConfig.java +++ b/src/main/java/at/hannibal2/skyhanni/config/features/mining/MiningConfig.java @@ -34,6 +34,11 @@ public class MiningConfig { public AreaWallsConfig crystalHollowsAreaWalls = new AreaWallsConfig(); @Expose + @ConfigOption(name = "Fossil Excavator", desc = "") + @Accordion + public FossilExcavatorConfig fossilExcavator = new FossilExcavatorConfig(); + + @Expose @ConfigOption(name = "Highlight Commission Mobs", desc = "Highlight Mobs that are part of active commissions.") @ConfigEditorBoolean @FeatureToggle diff --git a/src/main/java/at/hannibal2/skyhanni/features/mining/crystalhollows/CrystalHollowsWalls.kt b/src/main/java/at/hannibal2/skyhanni/features/mining/crystalhollows/CrystalHollowsWalls.kt index 914f8393a..08af9199f 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/mining/crystalhollows/CrystalHollowsWalls.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/mining/crystalhollows/CrystalHollowsWalls.kt @@ -16,7 +16,7 @@ import java.awt.Color class CrystalHollowsWalls { - val config get() = SkyHanniMod.feature.mining.crystalHollowsAreaWalls + private val config get() = SkyHanniMod.feature.mining.crystalHollowsAreaWalls fun isEnabled() = config.enabled && IslandType.CRYSTAL_HOLLOWS.isInIsland() diff --git a/src/main/java/at/hannibal2/skyhanni/features/mining/fossilexcavator/FossilExcavator.kt b/src/main/java/at/hannibal2/skyhanni/features/mining/fossilexcavator/FossilExcavator.kt new file mode 100644 index 000000000..e1c46e12e --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/features/mining/fossilexcavator/FossilExcavator.kt @@ -0,0 +1,243 @@ +package at.hannibal2.skyhanni.features.mining.fossilexcavator + +import at.hannibal2.skyhanni.SkyHanniMod +import at.hannibal2.skyhanni.SkyHanniMod.Companion.coroutineScope +import at.hannibal2.skyhanni.data.IslandType +import at.hannibal2.skyhanni.events.GuiContainerEvent +import at.hannibal2.skyhanni.events.GuiRenderEvent +import at.hannibal2.skyhanni.events.InventoryCloseEvent +import at.hannibal2.skyhanni.events.InventoryFullyOpenedEvent +import at.hannibal2.skyhanni.events.LorenzTickEvent +import at.hannibal2.skyhanni.events.LorenzWorldChangeEvent +import at.hannibal2.skyhanni.events.RenderInventoryItemTipEvent +import at.hannibal2.skyhanni.utils.InventoryUtils +import at.hannibal2.skyhanni.utils.ItemUtils.getLore +import at.hannibal2.skyhanni.utils.LorenzColor +import at.hannibal2.skyhanni.utils.LorenzUtils.isInIsland +import at.hannibal2.skyhanni.utils.LorenzUtils.round +import at.hannibal2.skyhanni.utils.RenderUtils.highlight +import at.hannibal2.skyhanni.utils.RenderUtils.renderString +import at.hannibal2.skyhanni.utils.RenderUtils.renderStrings +import at.hannibal2.skyhanni.utils.StringUtils.matchMatcher +import at.hannibal2.skyhanni.utils.StringUtils.removeColor +import at.hannibal2.skyhanni.utils.repopatterns.RepoPattern +import kotlinx.coroutines.launch +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent + +object FossilExcavator { + + private val config get() = SkyHanniMod.feature.mining.fossilExcavator + + private val patternGroup = RepoPattern.group("mining.fossilexcavator") + private val chargesRemainingPattern by patternGroup.pattern( + "chargesremaining", + "Chisel Charges Remaining: (?<charges>\\d+)" + ) + private val fossilProgressPattern by patternGroup.pattern( + "fossilprogress", + "Fossil Excavation Progress: (?<progress>[\\d.]+%)" + ) + + private var inInventory = false + private var inExcavatorMenu = false + + private var foundPercentage = false + private var percentage: String? = null + + var maxCharges = 0 + private var chargesRemaining = 0 + private var possibleFossilsRemaining = 0 + + private var slotToClick: Int? = null + private var correctPercentage: String? = null + + private var isNotPossible = false + private var isCompleted = false + + private var inventoryItemNames = listOf<String>() + + private const val NOT_POSSIBLE_STRING = "§cNo possible fossils on board." + private const val SOLVED_STRING = "§aFossil found, get all the loot you can." + private const val FOSSILS_REMAINING_STRING = "§ePossible fossils remaining: " + private const val CHARGES_REMAINING_STRING = "§eCharges remaining: " + + var possibleFossilTypes = setOf<FossilType>() + + @SubscribeEvent + fun onInventoryOpen(event: InventoryFullyOpenedEvent) { + if (!isEnabled()) return + if (event.inventoryName != "Fossil Excavator") return + inInventory = true + } + + @SubscribeEvent + fun onWorldChange(event: LorenzWorldChangeEvent) { + clearData() + } + + @SubscribeEvent + fun onInventoryClose(event: InventoryCloseEvent) { + clearData() + } + + private fun clearData() { + inInventory = false + inExcavatorMenu = false + foundPercentage = false + percentage = null + chargesRemaining = 0 + slotToClick = null + correctPercentage = null + isNotPossible = false + isCompleted = false + inventoryItemNames = emptyList() + possibleFossilTypes = emptySet() + } + + @SubscribeEvent + fun onTick(event: LorenzTickEvent) { + if (!isEnabled()) return + if (!inInventory) return + val slots = InventoryUtils.getItemsInOpenChest() + val itemNames = slots.map { it.stack.displayName.removeColor() } + if (itemNames != inventoryItemNames) { + inventoryItemNames = itemNames + inExcavatorMenu = itemNames.any { it == "Start Excavator" } + if (inExcavatorMenu) return + + updateData() + } + } + + private fun updateData() { + val fossilLocations = mutableSetOf<Int>() + val dirtLocations = mutableSetOf<Int>() + + var foundChargesRemaining = false + for (slot in InventoryUtils.getItemsInOpenChest()) { + val stack = slot.stack + val slotIndex = slot.slotIndex + val stackName = stack.displayName.removeColor() + val isDirt = stackName == "Dirt" + val isFossil = stackName == "Fossil" + when { + isDirt -> dirtLocations.add(slotIndex) + isFossil -> fossilLocations.add(slotIndex) + else -> continue + } + + if (!foundChargesRemaining) { + for (line in stack.getLore()) { + chargesRemainingPattern.matchMatcher(line.removeColor()) { + chargesRemaining = group("charges").toInt() + if (maxCharges == 0) maxCharges = chargesRemaining + foundChargesRemaining = true + } + } + } + + if (!isFossil || foundPercentage) continue + for (line in stack.getLore()) { + fossilProgressPattern.matchMatcher(line.removeColor()) { + foundPercentage = true + percentage = group("progress") + } + } + } + + coroutineScope.launch { + FossilExcavatorSolver.findBestTile(fossilLocations, dirtLocations, percentage) + } + } + + @SubscribeEvent + fun onSlotClick(event: GuiContainerEvent.SlotClickEvent) { + if (!isEnabled()) return + if (!inInventory) return + if (inExcavatorMenu) return + + event.makePickblock() + + val slot = event.slot ?: return + if (slot.slotIndex == slotToClick) { + slotToClick = null + correctPercentage = null + } + } + + @SubscribeEvent + fun onBackgroundDrawn(event: GuiContainerEvent.BackgroundDrawnEvent) { + if (!isEnabled()) return + if (!inInventory) return + if (inExcavatorMenu) return + if (slotToClick == null) return + + for (slot in InventoryUtils.getItemsInOpenChest()) { + if (slot.slotIndex == slotToClick) { + slot highlight LorenzColor.GREEN + } + } + } + + @SubscribeEvent + fun onRenderItemTip(event: RenderInventoryItemTipEvent) { + if (!isEnabled()) return + if (!inInventory) return + if (!config.showPercentage) return + if (slotToClick != event.slot.slotNumber) return + if (inExcavatorMenu) return + val correctPercentage = correctPercentage ?: return + + event.stackTip = correctPercentage + event.offsetX = 10 + event.offsetY = 10 + } + + @SubscribeEvent + fun onBackgroundDraw(event: GuiRenderEvent.ChestGuiOverlayRenderEvent) { + if (!isEnabled()) return + if (!inInventory) return + + if (inExcavatorMenu) { + // render here so they can move it around. As if you press key while doing the excavator you lose the scrap + config.position.renderString("§eExcavator solver gui", posLabel = "Fossil Excavator") + return + } + + val displayList = mutableListOf<String>() + + when { + isNotPossible -> displayList.add(NOT_POSSIBLE_STRING) + isCompleted -> displayList.add(SOLVED_STRING) + else -> displayList.add("$FOSSILS_REMAINING_STRING§a$possibleFossilsRemaining") + } + displayList.add("$CHARGES_REMAINING_STRING§a$chargesRemaining") + + if (possibleFossilTypes.isNotEmpty()) { + displayList.add("§ePossible Fossil types:") + for (fossil in possibleFossilTypes) { + displayList.add("§7- ${fossil.displayName}") + } + } + + config.position.renderStrings(displayList, posLabel = "Fossil Excavator") + } + + fun nextData(slotToClick: FossilTile, correctPercentage: Double, fossilsRemaining: Int) { + val formattedPercentage = (correctPercentage * 100).round(1) + + this.possibleFossilsRemaining = fossilsRemaining + this.slotToClick = slotToClick.toSlotIndex() + this.correctPercentage = "§2$formattedPercentage%" + } + + fun showError() { + isNotPossible = true + } + + fun showCompleted() { + isCompleted = true + } + + private fun isEnabled() = IslandType.DWARVEN_MINES.isInIsland() && config.enabled +} diff --git a/src/main/java/at/hannibal2/skyhanni/features/mining/fossilexcavator/FossilExcavatorSolver.kt b/src/main/java/at/hannibal2/skyhanni/features/mining/fossilexcavator/FossilExcavatorSolver.kt new file mode 100644 index 000000000..b12e33674 --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/features/mining/fossilexcavator/FossilExcavatorSolver.kt @@ -0,0 +1,146 @@ +package at.hannibal2.skyhanni.features.mining.fossilexcavator + +object FossilExcavatorSolver { + /* + to be used when they have less than 18 clicks + - solves 361/404 in at most 16 clicks + - solves 396/404 in at most 17 clicks + - solves 400/404 in at most 18 clicks + This is why it is not used all the time + */ + private val riskyStartingSequence: Set<Triple<FossilTile, Double, Int>> = setOf( + Triple(FossilTile(4, 2), 0.515, 404), + Triple(FossilTile(5, 3), 0.393, 196), + Triple(FossilTile(3, 2), 0.513, 119), + Triple(FossilTile(7, 2), 0.345, 58), + Triple(FossilTile(1, 3), 0.342, 38), + Triple(FossilTile(3, 4), 0.6, 25), + Triple(FossilTile(5, 1), 0.8, 10), + Triple(FossilTile(4, 3), 1.0, 2), + ) + + // once they have 18 chisels solves all in 18 clicks + private val safeStartingSequence: Set<Triple<FossilTile, Double, Int>> = setOf( + Triple(FossilTile(4, 2), 0.515, 404), + Triple(FossilTile(5, 4), 0.413, 196), + Triple(FossilTile(3, 3), 0.461, 115), + Triple(FossilTile(5, 2), 0.387, 62), + Triple(FossilTile(3, 1), 0.342, 38), + Triple(FossilTile(7, 3), 0.48, 25), + Triple(FossilTile(1, 2), 0.846, 13), + Triple(FossilTile(3, 4), 1.0, 2), + ) + + private var currentlySolving = false + + private fun getCurrentSequence(): Set<Triple<FossilTile, Double, Int>> { + return if (FossilExcavator.maxCharges < 18) { + riskyStartingSequence + } else { + safeStartingSequence + } + } + + private fun isPositionInStartSequence(position: FossilTile): Boolean { + return getCurrentSequence().any { it.first == position } + } + + fun findBestTile(fossilLocations: Set<Int>, dirtLocations: Set<Int>, percentage: String?) { + if (currentlySolving) return + currentlySolving = true + + val invalidPositions: MutableSet<FossilTile> = mutableSetOf() + for (i in 0..53) { + if (i !in fossilLocations && i !in dirtLocations) { + invalidPositions.add(FossilTile(i)) + } + } + val foundPositions = fossilLocations.map { FossilTile(it) }.toSet() + + val needsMoveSequence = foundPositions.isEmpty() && invalidPositions.all { isPositionInStartSequence(it) } + + if (needsMoveSequence) { + val movesTaken = invalidPositions.size + if (movesTaken >= getCurrentSequence().size) { + FossilExcavator.showError() + currentlySolving = false + return + } + + val nextMove = getCurrentSequence().elementAt(movesTaken) + FossilExcavator.nextData(nextMove.first, nextMove.second, nextMove.third) + currentlySolving = false + return + } + + val possibleClickPositions: MutableMap<FossilTile, Int> = mutableMapOf() + var totalPossibleTiles = 0.0 + + val possibleFossilTypes = if (percentage == null) FossilType.entries else { + val possibleFossils = FossilType.getByPercentage(percentage) + FossilExcavator.possibleFossilTypes = possibleFossils.toSet() + possibleFossils + } + + for (x in 0..8) { + for (y in 0..5) { + for (fossil in possibleFossilTypes) { + for (mutation in fossil.possibleMutations) { + val newPosition = mutation.modification(fossil.fossilShape).moveTo(x, y) + if (!isValidFossilPosition(newPosition, invalidPositions, foundPositions)) { + continue + } + + totalPossibleTiles++ + for (position in newPosition.tiles) { + possibleClickPositions.compute(position) { _, v -> v?.plus(1) ?: 1 } + } + } + } + } + } + + possibleClickPositions.filter { it.key in foundPositions }.forEach { + possibleClickPositions.remove(it.key) + } + + val bestPosition = possibleClickPositions.maxByOrNull { it.value } ?: run { + if (fossilLocations.isNotEmpty()) { + FossilExcavator.showCompleted() + currentlySolving = false + return + } + + FossilExcavator.showError() + currentlySolving = false + return + } + + val nextMove = bestPosition.key + val correctPercentage = bestPosition.value / totalPossibleTiles + currentlySolving = false + FossilExcavator.nextData(nextMove, correctPercentage, totalPossibleTiles.toInt()) + } + + private fun isValidFossilPosition( + fossil: FossilShape, + invalidPositions: Set<FossilTile>, + foundPositions: Set<FossilTile> + ): Boolean { + if (fossil.tiles.any { !isValidPosition(it, invalidPositions) }) { + return false + } + + for (pos in foundPositions) { + if (!fossil.tiles.contains(pos)) { + return false + } + } + return true + } + + private fun isValidPosition(fossil: FossilTile, invalidPositions: Set<FossilTile>): Boolean { + if (fossil in invalidPositions) return false + return fossil.x >= 0 && fossil.y >= 0 && fossil.x < 9 && fossil.y < 6 + } +} diff --git a/src/main/java/at/hannibal2/skyhanni/features/mining/fossilexcavator/FossilMutation.kt b/src/main/java/at/hannibal2/skyhanni/features/mining/fossilexcavator/FossilMutation.kt new file mode 100644 index 000000000..f039b2ef0 --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/features/mining/fossilexcavator/FossilMutation.kt @@ -0,0 +1,21 @@ +package at.hannibal2.skyhanni.features.mining.fossilexcavator + +enum class FossilMutation(val modification: (FossilShape) -> FossilShape) { + ROTATE_0({ positions -> positions.rotate(0) }), + ROTATE_90({ positions -> positions.rotate(90) }), + ROTATE_180({ positions -> positions.rotate(180) }), + ROTATE_270({ positions -> positions.rotate(270) }), + FLIP_ROTATE_0({ positions -> positions.rotate(0).flipShape() }), + FLIP_ROTATE_90({ positions -> positions.rotate(90).flipShape() }), + FLIP_ROTATE_180({ positions -> positions.rotate(180).flipShape() }), + FLIP_ROTATE_270({ positions -> positions.rotate(270).flipShape() }); + + companion object { + val onlyRotation = listOf( + ROTATE_0, + ROTATE_90, + ROTATE_180, + ROTATE_270 + ) + } +} diff --git a/src/main/java/at/hannibal2/skyhanni/features/mining/fossilexcavator/FossilShape.kt b/src/main/java/at/hannibal2/skyhanni/features/mining/fossilexcavator/FossilShape.kt new file mode 100644 index 000000000..c30bb9175 --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/features/mining/fossilexcavator/FossilShape.kt @@ -0,0 +1,26 @@ +package at.hannibal2.skyhanni.features.mining.fossilexcavator + +data class FossilShape(val tiles: List<FossilTile>) { + fun width() = tiles.maxOf { it.x } - tiles.minOf { it.x } + fun height() = tiles.maxOf { it.y } - tiles.minOf { it.y } + + fun moveTo(x: Int, y: Int): FossilShape { + return FossilShape(tiles.map { FossilTile(it.x + x, it.y + y) }) + } + + fun rotate(degree: Int): FossilShape { + val width = this.width() + val height = this.height() + return when (degree) { + 90 -> FossilShape(tiles.map { FossilTile(it.y, width - it.x) }) + 180 -> FossilShape(tiles.map { FossilTile(width - it.x, height - it.y) }) + 270 -> FossilShape(tiles.map { FossilTile(height - it.y, it.x) }) + else -> this + } + } + + fun flipShape(): FossilShape { + val height = this.height() + return FossilShape(tiles.map { FossilTile(it.x, height - it.y) }) + } +} diff --git a/src/main/java/at/hannibal2/skyhanni/features/mining/fossilexcavator/FossilTile.kt b/src/main/java/at/hannibal2/skyhanni/features/mining/fossilexcavator/FossilTile.kt new file mode 100644 index 000000000..1c6ba7070 --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/features/mining/fossilexcavator/FossilTile.kt @@ -0,0 +1,8 @@ +package at.hannibal2.skyhanni.features.mining.fossilexcavator + +data class FossilTile(val x: Int, val y: Int) { + + constructor(slotIndex: Int) : this(slotIndex % 9, slotIndex / 9) + + fun toSlotIndex() = x + y * 9 +} diff --git a/src/main/java/at/hannibal2/skyhanni/features/mining/fossilexcavator/FossilType.kt b/src/main/java/at/hannibal2/skyhanni/features/mining/fossilexcavator/FossilType.kt new file mode 100644 index 000000000..208dfac96 --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/features/mining/fossilexcavator/FossilType.kt @@ -0,0 +1,187 @@ +package at.hannibal2.skyhanni.features.mining.fossilexcavator + +enum class FossilType( + val displayName: String, + val totalTiles: Int, + val firstPercentage: String, + val fossilShape: FossilShape, + val possibleMutations: List<FossilMutation> +) { + TUSK( + "Tusk", 8, "12.5%", + FossilShape( + listOf( + FossilTile(0, 2), + FossilTile(0, 3), + FossilTile(0, 4), + FossilTile(1, 1), + FossilTile(2, 0), + FossilTile(3, 1), + FossilTile(3, 3), + FossilTile(4, 2) + ) + ), + FossilMutation.entries + ), + WEBBED( + "Webbed", 10, "10%", + FossilShape( + listOf( + FossilTile(0, 2), + FossilTile(1, 1), + FossilTile(2, 0), + FossilTile(3, 0), + FossilTile(3, 1), + FossilTile(3, 2), + FossilTile(3, 3), + FossilTile(4, 0), + FossilTile(5, 1), + FossilTile(6, 2), + ) + ), + listOf( + FossilMutation.ROTATE_0, + FossilMutation.FLIP_ROTATE_0, + ) + ), + CLUB( + "Club", 11, "9.1%", + FossilShape( + listOf( + FossilTile(0, 2), + FossilTile(0, 3), + FossilTile(1, 2), + FossilTile(1, 3), + FossilTile(2, 1), + FossilTile(3, 0), + FossilTile(4, 0), + FossilTile(5, 0), + FossilTile(6, 0), + FossilTile(6, 2), + FossilTile(7, 1), + ) + ), + listOf( + FossilMutation.ROTATE_0, + FossilMutation.ROTATE_180, + FossilMutation.FLIP_ROTATE_0, + FossilMutation.FLIP_ROTATE_180, + ) + ), + SPINE( + "Spine", 12, "8.3%", + FossilShape( + listOf( + FossilTile(0, 2), + FossilTile(1, 1), + FossilTile(1, 2), + FossilTile(2, 0), + FossilTile(2, 1), + FossilTile(2, 2), + FossilTile(3, 0), + FossilTile(3, 1), + FossilTile(3, 2), + FossilTile(4, 1), + FossilTile(4, 2), + FossilTile(5, 2), + ) + ), + FossilMutation.onlyRotation + ), + CLAW( + "Claw", 13, "7.7%", + FossilShape( + listOf( + FossilTile(0, 3), + FossilTile(1, 2), + FossilTile(1, 4), + FossilTile(2, 1), + FossilTile(2, 3), + FossilTile(3, 1), + FossilTile(3, 2), + FossilTile(3, 4), + FossilTile(4, 0), + FossilTile(4, 1), + FossilTile(4, 2), + FossilTile(4, 3), + FossilTile(5, 1), + ) + ), + FossilMutation.entries + ), + FOOTPRINT( + "Footprint", 13, "7.7%", + FossilShape( + listOf( + FossilTile(0, 2), + FossilTile(1, 1), + FossilTile(1, 2), + FossilTile(1, 3), + FossilTile(2, 1), + FossilTile(2, 2), + FossilTile(2, 3), + FossilTile(3, 0), + FossilTile(3, 2), + FossilTile(3, 4), + FossilTile(4, 0), + FossilTile(4, 2), + FossilTile(4, 4), + ) + ), + FossilMutation.onlyRotation + ), + HELIX( + "Helix", 14, "7.1%", + FossilShape( + listOf( + FossilTile(0, 0), + FossilTile(0, 1), + FossilTile(0, 2), + FossilTile(0, 4), + FossilTile(1, 0), + FossilTile(1, 2), + FossilTile(1, 4), + FossilTile(2, 0), + FossilTile(2, 4), + FossilTile(3, 0), + FossilTile(3, 1), + FossilTile(3, 2), + FossilTile(3, 3), + FossilTile(3, 4), + ) + ), + FossilMutation.entries + ), + UGLY( + "Ugly", 16, "6.2%", + FossilShape( + listOf( + FossilTile(0, 1), + FossilTile(1, 0), + FossilTile(1, 1), + FossilTile(1, 2), + FossilTile(2, 0), + FossilTile(2, 1), + FossilTile(2, 2), + FossilTile(2, 3), + FossilTile(3, 0), + FossilTile(3, 1), + FossilTile(3, 2), + FossilTile(3, 3), + FossilTile(4, 0), + FossilTile(4, 1), + FossilTile(4, 2), + FossilTile(5, 1), + ) + ), + FossilMutation.onlyRotation + ), + ; + + companion object { + fun getByPercentage(percentage: String): MutableList<FossilType> { + return entries.filter { it.firstPercentage == percentage }.toMutableList() + } + } +} + |