Skip to content

Instantly share code, notes, and snippets.

@kotarou3
Last active September 27, 2022 05:38
Show Gist options
  • Save kotarou3/69b39b79a13b71e9683be33a00cda327 to your computer and use it in GitHub Desktop.
Save kotarou3/69b39b79a13b71e9683be33a00cda327 to your computer and use it in GitHub Desktop.

Fully automated island sanctuary

Where you only have to check back once per week to continue the automation. No manual gathering required!

Configuration

  • Granaries: Fatal Falls + Mossy Mountains
  • Workshop Preset 1: Brush → Garden Scythe → Porcelain Vase → Garden Scythe
  • Workshop Preset 2: Firesand → Brick Counter → Garnet Rapier → Hora
  • Workshop Preset 3: Hora → Garnet Rapier → Hora → Tomato Relish
  • Workshop Preset 4: Firesand → Garnet Rapier → Hora → Caramels
  • Workshop Preset 5: Firesand → Porcelain Vase → Garden Scythe → Caramels
  • Workshop Preset 6: Firesand → Potion → Firesand → Quartz Ring → Firesand
  • Cropland: 1× Tomato + 19× Anything (Pick ones where you can buy the seed)
  • Pasture: Animals that produce 24× Claw + 22× Carapace + 8× Milk + 3× Fur (See below for an example)

Alternate the workshop schedule between presets 1,2,3 and 4,5,6, but always starting on presets 1,2,3 for the first non-rest day of the week. This means that presets 1,2,3 will be crafted 3 times per week, while 4,5,6 twice per week.
Rest days can be placed on any day you prefer

You will need to build up a stockpile of all the materials needed in a single week to be able to ignore the island for the week. But you can build the stockpile even while using this configuration - it just means you have to go to the island more often to collect materials

Example pastures:

  • Common animals only: 4× Glyptodon Pup + 2× Aurochs + 1× Squirrel
  • Mix of rare animals: Beachcomb + Buffalo + Coblyn + Glyptodon + Karakul + Marmot + Paissa + Yellow Coblyn

Profit

Average of 10319 blue cowries per week:

  • +8975: Workshop crafts
  • +2340: Selling 195.0× excess leavings @ 12 each
  • +944: Selling 157.3× excess produce @ 6 each
  • +700: Selling 28× excess silver ore @ 25 each
  • +300: Selling 12× excess garnet @ 25 each
  • -1400: Automated pasture costs
  • -700: Granary costs
  • -700: Automated cropland costs
  • -140: Cropland seed costs

Workshop crafts revenue assumes that the multiplier from random variation in popularity and supply averages out to 1 over multiple days and weeks, but otherwise tries to take into account other multipliers, including forced supply shift due to making too much of a single item. See main.py:scheduleRevenue() for details

195 excess leavings assumes you use premium feed, which results in a 80% chance on the "rare" leaving. So from this 252 total leavings, subtract 57 used for the workshop, leaving us with 195 excess to sell

157.3 excess produce also assumes you use premium feed. From the 350 total produce per week, subtract (140 feed)÷(3 per craft)×(4 produce per craft)=186.7 for feeding the pasture animals, and 6 for the workshop, leaving us with 157.3 excess to sell

Methodology

For every possible granary destination pair, main.py constructs all possible workshop agendas for a single workshop on a single day. It then combines the agendas into 6 or less presets using both a greedy algorithm (enumerate all combinations of the top 200 agendas) and a knapsack problem solver, and picks the best few in terms of revenue. I then copied the best result into this document

Unfortunately, since this problem is NP-hard, this might not be the optimal solution. But it should be close

If you can find a better solution, let me know!

(Why 6 or less presets and not more? Because I wanted to limit the runtime, and 3 on the greedy and 6 on the knapsack solver seemed reasonable. But perhaps I will try higher numbers in the future...)

import collections, csv, pandas
from dataclasses import dataclass
from typing import Annotated, NewType
ItemName = NewType("ItemName", str)
Source = NewType("Source", str)
itemToSource: dict[ItemName, set[Source]] = {}
sourceToItem: dict[Source, set[ItemName]] = {}
granary: set[Source] = set()
rareMaterial: set[ItemName] = set()
# Crops where the seeds must be gathered, rather than bought
gatheredCrop: dict[Annotated[ItemName, "crop"], Annotated[ItemName, "seed"]] = {}
# workshopCrafts[:].materials and workshopCraftsMaterials have their indicies
# pre-aligned so computations involving them can skip index alignment (which
# would be a large performance penalty)
@dataclass
class WorkshopCraft:
item: ItemName
levelReq: int
themes: set[str]
materials: pandas.Series # pandas.Series[ItemName, float]
craftingTime: int
value: int
workshopCrafts: dict[ItemName, WorkshopCraft] = {}
workshopCraftsMaterials: pandas.DataFrame # pandas.DataFrame[Annotate[ItemName, "material"], Annotated[ItemName, "craft"], float]
grooveCap: dict[Annotated[int, "landmark count"], int] = {}
workshopRankValueMod: dict[Annotated[int, "rank"], float] = {}
def supplyValueMod(supply: int) -> float:
# Based on MJICraftworksSupplyDefine.exd but:
# - Fitted to a continuous function
# - Translated and flipped such the modifier represents the change in
# value compared to supply=0 ("sufficient")
return 1.03**-supply
def init() -> None:
def addItemSource(item: ItemName, source: Source) -> None:
itemToSource.setdefault(item, set()).add(source)
sourceToItem.setdefault(source, set()).add(item)
prefixToSource: dict[str, Source] = {}
for row in csv.DictReader(open("MJIStockyardManagementArea.csv")):
item = ItemName(row["Rare Material"])
source = Source(row["Area"])
addItemSource(item, source)
granary.add(source)
rareMaterial.add(item)
prefixToSource[row["#"]] = source
for row in csv.DictReader(open("MJIStockyardManagementTable.csv")):
item = ItemName(row["Material"])
source = prefixToSource[row["#"].split(".")[0]]
addItemSource(item, source)
for row in csv.DictReader(open("MJIItemPouch.csv")):
item = ItemName(row["Item"])
if row["Crop"]:
crop = ItemName(row["Crop"])
if itemToSource.setdefault(item, set()) - {"Cropland"}:
gatheredCrop[crop] = item
if row["Category"] == "Produce":
addItemSource(item, Source("Cropland"))
if row["Category"] == "Leavings":
addItemSource(item, Source("Pasture"))
for row in csv.DictReader(open("MJICraftworksObject.csv")):
parsed = WorkshopCraft(
item = row["Item"],
levelReq = int(row["LevelReq"]),
themes = {row["Theme[0]"]},
craftingTime = int(row["CraftingTime"]),
value = int(row["Value"]),
materials = None,
)
materials = collections.Counter()
if not parsed.item:
continue
if row["Theme[1]"]:
parsed.themes.add(row["Theme[1]"])
if row["Amount[0]"] and int(row["Amount[0]"]) > 0:
materials[row["Material[0]"]] = int(row["Amount[0]"])
if row["Amount[1]"] and int(row["Amount[1]"]) > 0:
materials[row["Material[1]"]] = int(row["Amount[1]"])
if row["Amount[2]"] and int(row["Amount[2]"]) > 0:
materials[row["Material[2]"]] = int(row["Amount[2]"])
if row["Amount[3]"] and int(row["Amount[3]"]) > 0:
materials[row["Material[3]"]] = int(row["Amount[3]"])
for crop, seed in gatheredCrop.items():
if materials[crop]:
materials[seed] = materials[crop] / 5
for item, count in list(materials.items()):
if itemToSource[item] == {"Cropland"}:
materials["Produce"] += count
elif itemToSource[item] == {"Pasture"}:
materials["Leavings"] += count
parsed.materials = pandas.Series(materials, name=parsed.item, dtype=float)
workshopCrafts[parsed.item] = parsed
global workshopCraftsMaterials
workshopCraftsMaterials = pandas.DataFrame([c.materials for c in workshopCrafts.values()]).fillna(0).T
for craft in workshopCrafts.values():
craft.materials = workshopCraftsMaterials.loc[:, craft.item]
for row in csv.DictReader(open("MJICraftworksTension.csv")):
grooveCap[int(row["#"])] = int(row["Groove Cap"])
for row in csv.DictReader(open("MJICraftworksRankRatio.csv")):
workshopRankValueMod[int(row["#"])] = int(row["Ratio"]) / 100
init()
#!/usr/bin/env python3
import collections, data, itertools, numpy, pandas
from dataclasses import dataclass
from ortools.algorithms import pywrapknapsack_solver
from typing import Annotated, NewType, TypeAlias
workshopRank = 3
landmarkCount = 4
# Max number of a single produce/leaving type that can be consumed in a week.
# This is further capped by the script to the physical limitations of the
# cropland/pasture (see weeklyBudget()).
# Want a more diverse cropland/pasture? Reduce the numbers to taste
maxSingleProduce = 999 # 35 for perfectly balanced
maxSingleLeaving = 27 # 28 for perfectly balanced
################################################################################
# A sequence of items a single workshop crafts in a single day
Agenda = NewType("Agenda", list[data.ItemName])
# A combination of agendas to be split between multiple workshops and multiple
# days, but not allocated to individual workshops and days yet, so order doesn't
# matter
Combination = NewType("Combination", list[Agenda])
# Agendas allocated to specific workshops on specific days of the week
DaySchedule = NewType("DaySchedule", tuple[Agenda, Agenda, Agenda])
WeekSchedule = NewType("WeekSchedule", tuple[DaySchedule, DaySchedule, DaySchedule, DaySchedule, DaySchedule])
Budget: TypeAlias = pandas.Series # pandas.Series[ItemName, float]
Cost: TypeAlias = pandas.Series # pandas.Series[ItemName, float]
def agendaCost(agenda: Agenda) -> Cost:
return scheduleCost([[agenda]]) # pytype: disable=wrong-arg-types
def scheduleCost(schedule: WeekSchedule) -> Cost:
cost = []
for day in schedule:
for agenda in day:
for craft in agenda:
craft = data.workshopCrafts[craft]
cost.append(craft.materials)
return pandas.Series(
numpy.sum(cost, axis=0),
index=data.workshopCraftsMaterials.index,
name="Cost",
dtype=float,
)
def agendaRevenue(agenda: Agenda) -> float:
# Quick calculation that only cares about craft base value and double bonus.
# Ignores: popularity, supply, groove, workshop rank
assert(len(agenda) > 0)
prevCraft = data.workshopCrafts[agenda[0]]
revenue = prevCraft.value
for craft in agenda[1:]:
craft = data.workshopCrafts[craft]
revenue += craft.value
if craft.item != prevCraft.item and craft.themes & prevCraft.themes:
revenue += craft.value
prevCraft = craft
return revenue
def scheduleCombination(combination: Combination) -> WeekSchedule:
# Sorts combination by decreasing revenue, and:
# - If len(combination) <= 3: Repeats the agendas for every single day
# - If 3 < len(combination) <= 6: Alternates between combination[:3] and
# combination[3:6] for each day
assert(len(combination) <= 6)
combination.sort(key=agendaRevenue, reverse=True)
schedule = []
for day in range(5):
if len(combination) > 3 and day % 2 == 1:
schedule.append(DaySchedule(tuple(combination[3:6])))
else:
schedule.append(DaySchedule(tuple(combination[:3])))
return WeekSchedule(tuple(schedule))
@dataclass
class RevenueBreakdown:
total: float
@dataclass
class Item:
name: data.ItemName
revenue: float
by: str
hour: int
items: list[Item]
def scheduleRevenue(schedule: WeekSchedule, weeklyBudget: Budget, includeDetails: bool = False) -> RevenueBreakdown:
# Does a more detailed calculation taking into account:
# - Multipliers: double bonus, supply, groove, workshop rank
# - Direct selling of leftover budget
# Ignores the effects of popularity and random supply shift (assumes they
# average out to 1)
result = RevenueBreakdown(0, [])
groove = 0
itemCount = collections.Counter()
def sellCraft(craft: data.WorkshopCraft, by: str, hour: int) -> None:
revenue = (craft.value
* data.supplyValueMod(itemCount[craft.item])
* data.workshopRankValueMod[workshopRank]
* (1 + groove / 100))
result.total += revenue
if includeDetails:
result.items.append(RevenueBreakdown.Item(
name = craft.item,
revenue = revenue,
by = by,
hour = hour,
))
itemCount[craft.item] += 1
def workshopDay(agenda: Agenda, workshop: int, day: int) -> collections.abc.Iterator[None]:
nonlocal groove
prev = None
hour = 0
for craft in agenda:
craft = data.workshopCrafts[craft]
isBonus = prev and craft.item != prev.item and craft.themes & prev.themes
if isBonus and groove < data.grooveCap[landmarkCount]:
groove += 1
for _ in range(craft.craftingTime):
yield
hour += 1
sellCraft(craft, f"Workshop {workshop}", day * 24 + hour)
if isBonus:
sellCraft(craft, f"Workshop {workshop}", day * 24 + hour)
prev = craft
for d, day in enumerate(schedule):
workshops = [workshopDay(agenda, a + 1, d) for a, agenda in enumerate(day)]
for _ in itertools.zip_longest(*workshops):
pass
def sellOther(item: data.ItemName, units: float, unitPrice: float) -> None:
result.total += units * unitPrice
if includeDetails:
result.items.append(RevenueBreakdown.Item(
name = data.ItemName(f"{item} ×{units:.1f}"),
revenue = units * unitPrice,
by = "Exporter",
hour = len(schedule) * 24,
))
cost = scheduleCost(schedule)
assert(weeklyBudget.index.equals(cost.index))
leftover = weeklyBudget.to_numpy() - cost.to_numpy()
assert((leftover >= 0).all())
leftover = pandas.Series(leftover, index=cost.index, dtype=float)
if leftover["Produce"]:
sellOther(data.ItemName("Produce"), leftover["Produce"], 6)
if leftover["Leavings"]:
sellOther(data.ItemName("Leavings"), leftover["Leavings"], 12)
for item in data.rareMaterial:
if leftover[item] != 0:
sellOther(item, leftover[item], 25)
return result
def allAgendas(crafts: collections.abc.Collection[data.WorkshopCraft], maxHours: int = 24) -> collections.abc.Iterator[Agenda]:
for craft in crafts:
if craft.craftingTime > maxHours:
continue
yield Agenda([craft.item])
for agenda in allAgendas(crafts, maxHours - craft.craftingTime):
yield Agenda([craft.item] + agenda)
def allCombinations(agendaSpace: collections.abc.Collection[tuple[Agenda, Cost]], dailyBudget: Budget, maxDepth: int = 3) -> collections.abc.Iterator[Combination]:
# pandas doesn't vectorise binomial operations, so use numpy instead
numpyAgendaSpace = []
for agenda, cost in agendaSpace:
assert(cost.index.equals(dailyBudget.index))
numpyAgendaSpace.append((agenda, cost.to_numpy()))
return _allCombinations(numpyAgendaSpace, dailyBudget.to_numpy(), maxDepth)
def _allCombinations(agendaSpace: collections.abc.Collection[tuple[Agenda, numpy.ndarray]], dailyBudget: numpy.ndarray, maxDepth: int) -> collections.abc.Iterator[Combination]:
if maxDepth <= 0:
return
viableAgendas = []
newBudgets = []
for agenda, cost in agendaSpace:
newBudget = dailyBudget - cost
if (newBudget >= 0).all():
viableAgendas.append((agenda, cost))
newBudgets.append(newBudget)
for (agenda, cost), newBudget in zip(viableAgendas, newBudgets):
yield Combination([agenda])
for combination in _allCombinations(viableAgendas, newBudget, maxDepth - 1):
yield Combination([agenda] + combination)
def combinationKnapsackSolver(agendaSpace: collections.abc.Sequence[tuple[Agenda, Cost]], dailyBudget: Budget) -> Combination:
# Treat each possible agenda as items to fit into the combination knapsack.
# This won't necessarily get us the optimal solution since the knapsack
# solver won't be able to take into account dependencies between agendas
# like supply, groove, and selling of leftover goods. But it should get us
# a reasonably good solution
# Solve for 2 days worth at once + a fudge factor due to 2 not evenly
# dividing 5 crafting days
budgetAndCosts = [(2 * 5 / 6) * dailyBudget]
for agenda, cost in agendaSpace:
budgetAndCosts.append(cost)
budgetAndCosts = numpy.ceil(1000 * pandas.DataFrame(budgetAndCosts).fillna(0)).astype(int)
budgetAndCosts.loc[:, "Agenda"] = [6] + [1] * len(agendaSpace)
profits = [round(agendaRevenue(a[0])) for a in agendaSpace]
capacities = budgetAndCosts.iloc[0].to_numpy().tolist()
weights = budgetAndCosts.iloc[1:].T.to_numpy().tolist()
solver = pywrapknapsack_solver.KnapsackSolver(
pywrapknapsack_solver.KnapsackSolver.KNAPSACK_MULTIDIMENSION_SCIP_MIP_SOLVER,
"Knapsack Solver"
)
solver.Init(profits, weights, capacities)
solver.set_time_limit(60)
solver.Solve()
combination = []
for a in range(len(agendaSpace)):
if solver.BestSolutionContains(a):
combination.append(agendaSpace[a][0])
return Combination(combination)
def weeklyBudget(granaries: collections.abc.Collection[data.Source]) -> Budget:
budget = collections.Counter()
for granary in granaries:
for item in data.sourceToItem[granary]:
budget[item] += 4 # Average number gathered
budget["Produce"] = (5 * 20 / 2) - (20 / 3 * 4) # (grown) - (fed to pasture)
budget["Leavings"] = (20) + (0.8 * 20) # (common) + (rare)
for item in data.sourceToItem[data.Source("Cropland")]:
budget[item] = maxSingleProduce / 7
for item in data.sourceToItem[data.Source("Pasture")]:
budget[item] = maxSingleLeaving / 7
budget = pandas.Series(budget, name="Budget", dtype=float) * 7
_, aligned = data.workshopCraftsMaterials.align(budget, join="left", axis=0, fill_value=0)
assert(aligned.gt(0).sum() == budget.gt(0).sum())
assert(aligned.index.equals(data.workshopCraftsMaterials.index))
return aligned
def findOptimalCombinations() -> list[tuple[Annotated[float, "revenue"], Annotated[collections.abc.Collection[data.Source], "granaries"], Combination]]:
shortlist = {}
for granaries in itertools.combinations_with_replacement(sorted(data.granary), 2):
# We could run all granary combinations in parallel, but the total serial
# runtime is only ~2m so not worth to implement
budget = weeklyBudget(granaries)
print("Calculating:", " + ".join(granaries))
viableCrafts = []
for craft in data.workshopCrafts.values():
if (budget / 5).sub(craft.materials, fill_value=0).ge(0).all():
viableCrafts.append(craft)
agendaSpace = {}
for agenda in allAgendas(viableCrafts):
revenue = agendaRevenue(agenda)
key = tuple(sorted(agenda))
if revenue > agendaSpace.setdefault(key, (0,))[0]:
agendaSpace[key] = (revenue, agenda)
agendaSpace = [(e[1], agendaCost(e[1])) for e in sorted(agendaSpace.values(), key=lambda e: e[0], reverse=True)]
# Try both a greedy (brute force over top 200 agendas) and a knapsack
# solver.
# The knapsack solver tends to produce a better solution, but the greedy
# solver produces multiple solutions that can be used as alternatives
combinations = []
for combination in itertools.chain(
allCombinations(agendaSpace[:200], budget / 5),
[combinationKnapsackSolver(agendaSpace, budget / 5)],
):
schedule = scheduleCombination(combination)
combinations.append((scheduleRevenue(schedule, budget).total, combination))
combinations.sort(key=lambda e: e[0], reverse=True)
# Shortlist the top 5 combinations with unique material costs
n = 0
for revenue, combination in combinations:
schedule = scheduleCombination(combination)
key = tuple(scheduleCost(schedule).items())
if revenue > shortlist.setdefault(key, (0,))[0]:
shortlist[key] = (revenue, granaries, combination)
n += 1
if n >= 5:
break
print()
return list(sorted(shortlist.values(), key=lambda e: e[0], reverse=True)) # pytype: disable=bad-return-type
def printOptimalCombinations() -> None:
shortlist = findOptimalCombinations()
print("#####################")
print("# Shortlist Summary #")
print("#####################")
for _, granaries, combination in shortlist:
budget = weeklyBudget(granaries)
combination.sort(key=agendaRevenue, reverse=True)
schedule = scheduleCombination(combination)
print("Profit:", scheduleRevenue(schedule, budget).total
- 7 * 2 * 50 # Granary
- 7 * 20 * (5 + 2 / 2) # Cropland
- 7 * 20 * 10 # Pasture
)
print(" Granaries:", " + ".join(granaries))
for s, setting in enumerate(combination):
print(f" Preset {s + 1}:", " → ".join(setting))
cost = scheduleCost(schedule)
print(" Cost:", list(cost.loc[cost != 0].sort_index().items()))
print()
print("########################")
print("# Details of Top Entry #")
print("########################")
_, granaries, combination = shortlist[0]
budget = weeklyBudget(granaries)
schedule = scheduleCombination(combination)
print(budget.loc[budget != 0].sort_index())
print()
cost = scheduleCost(schedule)
print(cost.loc[cost != 0].sort_index())
print()
revenue = scheduleRevenue(schedule, budget, includeDetails=True)
print(f"Revenue: {revenue.total:.0f}")
print(f" {'Price':>6s} {'Name':35s} {'Hour':4s} {'By':10s}")
for item in revenue.items:
print(f" {item.revenue:6.1f} {item.name:35s} {item.hour:4d} {item.by:10s}")
printOptimalCombinations()
# Item Theme[0] Theme[1] Material[0] Amount[0] Material[1] Amount[1] Material[2] Amount[2] Material[3] Amount[3] LevelReq CraftingTime Value
0 0 Island Palm Leaf 0 Island Palm Leaf 0 Island Palm Leaf 0 Island Palm Leaf 0 0 0 0
1 Isleworks Potion Concoctions 0 Island Palm Leaf 2 Islewort 2 Island Palm Leaf 0 Island Palm Leaf 0 1 4 28
2 Isleworks Firesand Concoctions Unburied Treasures 0 Island Sand 2 Island Limestone 1 Islewort 1 Island Palm Leaf 0 1 4 28
3 Isleworks Wooden Chair Furnishings Woodworks 0 Island Log 4 Island Vine 2 Island Palm Leaf 0 Island Palm Leaf 0 1 6 42
4 Isleworks Grilled Clam Foodstuffs Marine Merchandise 0 Island Clam 2 Island Laver 2 Island Palm Leaf 0 Island Palm Leaf 0 1 4 28
5 Isleworks Necklace Accessories Woodworks 0 Island Branch 3 Island Vine 1 Island Palm Leaf 0 Island Palm Leaf 0 1 4 28
6 Isleworks Coral Ring Accessories Marine Merchandise 0 Island Coral 3 Island Vine 3 Island Palm Leaf 0 Island Palm Leaf 0 1 6 42
7 Isleworks Barbut Attire Metalworks 0 Island Copper Ore 3 Island Sand 3 Island Palm Leaf 0 Island Palm Leaf 0 1 6 42
8 Isleworks Macuahuitl Arms Woodworks 0 Island Palm Log 3 Island Stone 3 Island Palm Leaf 0 Island Palm Leaf 0 1 6 42
9 Isleworks Sauerkraut Preserved Food 0 Island Cabbage 1 Island Rock Salt 3 Island Palm Leaf 0 Island Palm Leaf 0 1 4 40
10 Isleworks Baked Pumpkin Foodstuffs 0 Island Pumpkin 1 Island Sap 3 Island Palm Leaf 0 Island Palm Leaf 0 1 4 40
11 Isleworks Tunic Attire Textiles 0 Sanctuary Fleece 2 Island Vine 4 Island Palm Leaf 0 Island Palm Leaf 0 1 6 72
12 Isleworks Culinary Knife Sundries Creature Creations 0 Sanctuary Claw 1 Island Palm Log 3 Island Palm Leaf 0 Island Palm Leaf 0 1 4 44
13 Isleworks Brush Sundries Woodworks 0 Sanctuary Fur 1 Island Palm Log 3 Island Palm Leaf 0 Island Palm Leaf 0 1 4 44
14 Isleworks Boiled Egg Foodstuffs Creature Creations 0 Sanctuary Egg 1 Island Laver 3 Island Palm Leaf 0 Island Palm Leaf 0 1 4 44
15 Isleworks Hora Arms Creature Creations 0 Sanctuary Carapace 2 Island Stone 4 Island Palm Leaf 0 Island Palm Leaf 0 1 6 72
16 Isleworks Earrings Accessories Creature Creations 0 Sanctuary Fang 1 Island Vine 3 Island Palm Leaf 0 Island Palm Leaf 0 1 4 44
17 Isleworks Butter Ingredients Creature Creations 0 Sanctuary Milk 1 Island Rock Salt 3 Island Palm Leaf 0 Island Palm Leaf 0 1 4 44
18 Isleworks Brick Counter Furnishings Unburied Treasures 0 Island Clay 2 Island Limestone 2 Island Palm Log 2 Island Palm Leaf 0 5 6 48
19 Bronze Sheep Furnishings Metalworks 0 Island Tinsand 3 Island Copper Ore 3 Island Log 2 Island Palm Leaf 0 5 8 64
20 Isleworks Growth Formula Concoctions 0 Island Alyssum 2 Islewort 3 Island Branch 3 Island Palm Leaf 0 5 8 136
21 Isleworks Garnet Rapier Arms Unburied Treasures 0 Raw Island Garnet 2 Island Copper Ore 3 Island Tinsand 3 Island Palm Leaf 0 5 8 136
22 Isleworks Spruce Round Shield Attire Woodworks 0 Island Spruce Log 2 Island Copper Ore 3 Island Stone 3 Island Palm Leaf 0 5 8 136
23 Isleworks Shark Oil Sundries Marine Merchandise 0 Island Hammerhead 2 Island Laver 3 Island Sap 3 Island Palm Leaf 0 5 8 136
24 Isleworks Silver Ear Cuffs Accessories Metalworks 0 Island Silver Ore 2 Island Tinsand 3 Island Coral 3 Island Palm Leaf 0 5 8 136
25 Isleworks Sweet Popoto Confections 0 Island Popoto 2 Sanctuary Milk 1 Island Sap 3 Island Palm Leaf 0 5 6 72
26 Isleworks Parsnip Salad Foodstuffs 0 Island Parsnip 2 Islewort 1 Island Sap 1 Island Palm Leaf 0 5 4 48
27 Isleworks Caramels Confections 0 Island Sugarcane 4 Sanctuary Milk 2 Island Palm Leaf 0 Island Palm Leaf 0 6 6 81
28 Isleworks Ribbon Accessories Textiles 0 Island Cotton Boll 2 Island Copper Ore 2 Island Vine 2 Island Palm Leaf 0 6 6 54
29 Isleworks Rope Sundries Textiles 0 Island Hemp 2 Islewort 1 Island Vine 1 Island Palm Leaf 0 6 4 36
30 Isleworks Cavalier's Hat Attire Textiles 0 Sanctuary Feather 2 Island Cotton Boll 2 Island Hemp 2 Island Palm Leaf 0 6 6 81
31 Isleworks Horn Sundries Creature Creations 0 Sanctuary Horn 2 Island Clay 2 Island Hemp 2 Island Palm Leaf 0 6 6 81
32 Isleworks Salt Cod Preserved Food Marine Merchandise 0 Islefish 4 Island Rock Salt 2 Island Palm Leaf 0 Island Palm Leaf 0 7 6 54
33 Isleworks Squid Ink Ingredients Marine Merchandise 0 Island Squid 2 Island Rock Salt 2 Island Palm Leaf 0 Island Palm Leaf 0 7 4 36
34 Isleworks Essential Draught Concoctions Marine Merchandise 0 Island Jellyfish 2 Island Palm Leaf 2 Island Laver 2 Island Palm Leaf 0 7 6 54
35 Isleberry Jam Ingredients 0 Isleberry 3 Island Sugarcane 2 Island Sap 1 Island Palm Leaf 0 7 6 78
36 Isleworks Tomato Relish Ingredients 0 Island Tomato 2 Island Sugarcane 1 Islewort 1 Island Palm Leaf 0 7 4 52
37 Isleworks Onion Soup Foodstuffs 0 Island Onion 3 Island Rock Salt 2 Islewort 1 Island Palm Leaf 0 7 6 78
38 Islefish Pie Confections Marine Merchandise 0 Island Wheat 3 Islefish 2 Island Sugarcane 1 Island Palm Leaf 0 7 6 78
39 Isleworks Corn Flakes Preserved Food 0 Island Corn 2 Island Sugarcane 2 Island Palm Leaf 0 Island Palm Leaf 0 7 4 52
40 Isleworks Pickled Radish Preserved Food 0 Island Radish 4 Island Apple 2 Island Sugarcane 2 Island Palm Leaf 0 7 8 104
41 Isleworks Iron Axe Arms Metalworks 0 Island Iron Ore 3 Island Log 3 Island Sand 2 Island Palm Leaf 0 8 8 72
42 Isleworks Quartz Ring Accessories Unburied Treasures 0 Island Quartz 3 Island Iron Ore 3 Island Stone 2 Island Palm Leaf 0 8 8 72
43 Isleworks Porcelain Vase Sundries Unburied Treasures 0 Island Leucogranite 3 Island Quartz 3 Island Clay 2 Island Palm Leaf 0 8 8 72
44 Isleworks Vegetable Juice Concoctions 0 Island Cabbage 3 Islewort 2 Island Laver 1 Island Palm Leaf 0 8 6 78
45 Isleworks Pumpkin Pudding Confections 0 Island Pumpkin 3 Sanctuary Egg 1 Sanctuary Milk 1 Island Palm Leaf 0 8 6 78
46 Isleworks Sheepfluff Rug Furnishings Creature Creations 0 Sanctuary Fleece 3 Island Cotton Boll 2 Island Hemp 1 Island Palm Leaf 0 8 6 90
47 Isleworks Garden Scythe Sundries Metalworks 0 Sanctuary Claw 3 Island Iron Ore 2 Island Palm Log 1 Island Palm Leaf 0 9 6 90
48 Isleworks Bed Furnishings Textiles 0 Sanctuary Fur 4 Island Cotton Boll 2 Island Log 2 Island Palm Leaf 0 9 8 120
49 Isleworks Scale Fingers Attire Creature Creations 0 Sanctuary Carapace 4 Island Iron Ore 2 Island Cotton Boll 2 Island Palm Leaf 0 9 8 120
50 Isleworks Crook Arms Woodworks 0 Sanctuary Fang 4 Island Quartz 2 Island Log 2 Island Palm Leaf 0 9 8 120
51 0 Island Palm Leaf 0 Island Palm Leaf 0 Island Palm Leaf 0 Island Palm Leaf 0 0 0 0
52 0 Island Palm Leaf 0 Island Palm Leaf 0 Island Palm Leaf 0 Island Palm Leaf 0 0 0 0
53 0 Island Palm Leaf 0 Island Palm Leaf 0 Island Palm Leaf 0 Island Palm Leaf 0 0 0 0
54 0 Island Palm Leaf 0 Island Palm Leaf 0 Island Palm Leaf 0 Island Palm Leaf 0 0 0 0
55 0 Island Palm Leaf 0 Island Palm Leaf 0 Island Palm Leaf 0 Island Palm Leaf 0 0 0 0
56 0 Island Palm Leaf 0 Island Palm Leaf 0 Island Palm Leaf 0 Island Palm Leaf 0 0 0 0
57 0 Island Palm Leaf 0 Island Palm Leaf 0 Island Palm Leaf 0 Island Palm Leaf 0 0 0 0
58 0 Island Palm Leaf 0 Island Palm Leaf 0 Island Palm Leaf 0 Island Palm Leaf 0 0 0 0
59 0 Island Palm Leaf 0 Island Palm Leaf 0 Island Palm Leaf 0 Island Palm Leaf 0 0 0 0
60 0 Island Palm Leaf 0 Island Palm Leaf 0 Island Palm Leaf 0 Island Palm Leaf 0 0 0 0
61 0 Island Palm Leaf 0 Island Palm Leaf 0 Island Palm Leaf 0 Island Palm Leaf 0 0 0 0
# Ratio
0 0
1 100
2 110
3 120
4 130
# Groove Cap
0 10
1 15
2 20
3 25
4 35
5 45
# Item Category Crop
0 Island Palm Leaf Material
1 Island Apple Material
2 Island Branch Material
3 Island Stone Material
4 Island Clam Material
5 Island Laver Material
6 Island Coral Material
7 Islewort Material
8 Island Sand Material
9 Island Log Material
10 Island Palm Log Material
11 Island Vine Material
12 Island Sap Material
13 Island Copper Ore Material
14 Island Limestone Material
15 Island Rock Salt Material
16 Island Sugarcane Material
17 Island Cotton Boll Material
18 Island Hemp Material
19 Island Clay Material
20 Island Tinsand Material
21 Island Iron Ore Material
22 Island Quartz Material
23 Island Leucogranite Material
24 Islefish Material
25 Island Squid Material
26 Island Jellyfish Material
27 Island Alyssum Material
28 Raw Island Garnet Material
29 Island Spruce Log Material
30 Island Hammerhead Material
31 Island Silver Ore Material
32 Island Popoto Set Gardening Starter Island Popoto
33 Island Cabbage Seeds Gardening Starter Island Cabbage
34 Isleberry Seeds Gardening Starter Isleberry
35 Island Pumpkin Seeds Gardening Starter Island Pumpkin
36 Island Onion Set Gardening Starter Island Onion
37 Island Tomato Seeds Gardening Starter Island Tomato
38 Island Wheat Seeds Gardening Starter Island Wheat
39 Island Corn Seeds Gardening Starter Island Corn
40 Island Parsnip Seeds Gardening Starter Island Parsnip
41 Island Radish Seeds Gardening Starter Island Radish
42 Island Popoto Produce
43 Island Cabbage Produce
44 Isleberry Produce
45 Island Pumpkin Produce
46 Island Onion Produce
47 Island Tomato Produce
48 Island Wheat Produce
49 Island Corn Produce
50 Island Parsnip Produce
51 Island Radish Produce
52 Sanctuary Fleece Leavings
53 Sanctuary Claw Leavings
54 Sanctuary Fur Leavings
55 Sanctuary Feather Leavings
56 Sanctuary Egg Leavings
57 Sanctuary Carapace Leavings
58 Sanctuary Fang Leavings
59 Sanctuary Horn Leavings
60 Sanctuary Milk Leavings
61 Island Sweetfeed Feed
62 Island Greenfeed Feed
63 Premium Island Greenfeed Feed
64 Makeshift Net Restraint
65 Makeshift Restraint Restraint
66 Makeshift Soporific Restraint
# Rare Material Area
0 Island Alyssum 0 Meandering Meadows
1 Raw Island Garnet 1 Fatal Falls
2 Island Spruce Log 2 Wild Woods
3 Island Hammerhead 3 Bending Beaches
4 Island Silver Ore 4 Mossy Mountains
# Material
0.0 Island Apple
0.1 Island Stone
0.2 Islewort
0.3 Island Vine
0.4 Island Log
0.5 Island Cotton Boll
0.6 Island Popoto Set
0.7 Island Parsnip Seeds
1.0 Island Palm Leaf
1.1 Island Stone
1.2 Islewort
1.3 Island Sand
1.4 Island Palm Log
1.5 Island Sugarcane
1.6 Island Clay
1.7 Island Tinsand
2.0 Island Branch
2.1 Island Pumpkin Seeds
2.2 Island Cabbage Seeds
2.3 Islewort
2.4 Island Sap
2.5 Island Vine
2.6 Island Apple
2.7 Island Log
2.8 Island Hemp
3.0 Island Palm Leaf
3.1 Island Sand
3.2 Island Clam
3.3 Island Laver
3.4 Island Coral
3.5 Island Palm Log
3.6 Island Rock Salt
3.7 Islefish
3.8 Island Squid
3.9 Island Jellyfish
4.0 Island Branch
4.1 Island Stone
4.2 Island Sand
4.3 Island Copper Ore
4.4 Island Limestone
4.5 Island Iron Ore
4.6 Island Quartz
4.7 Island Leucogranite
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment