AI: adjust balance + HUMAN: add time to show the pattern and fix a filename

This commit is contained in:
2026-10-09 11:01:50 -04:00
parent 66802216fd
commit 8d742b89cf
4 changed files with 64 additions and 7 deletions
+51
View File
@@ -5,6 +5,57 @@ Hard fish should be more difficult AND rarer to catch than easy fish (easy = low
Response: Response:
I have the full picture now. Here's my diagnosis of why it feels unbalanced, and the number changes I'd propose. I won't touch anything until you approve.
Why it feels broken
1. Hard fish barely appear. Your weight is (max_difficulty - difficulty + 1) ** 0.5. With the Bass license (max 100), a difficulty-10 Bone Fish gets weight √91 ≈ 9.5, while the difficulty-100 Hammerhead gets √1 = 1. That's a ~9.5x gap — hard fish are extremely rare. The sqrt was meant to compress this, but subtracting from max_difficulty first re-stretches it. This is the main "I never see hard fish" cause.
2. The difficulty→skill-check mapping is brutally nonlinear. Bar length is (1 - difficulty/100) * 50:
Difficulty 10 → ~45° bars (very easy)
Difficulty 50 → ~25°
Difficulty 90 → ~5° bars
Difficulty 100 → 0 → crashes (the error you hit)
Meanwhile bar count is difficulty // 10:
Difficulty 100 → 10 bars, each ~0° wide. Mathematically impossible.
Difficulty 90 → 9 bars at ~5°.
So the top-tier fish are simultaneously max-count AND min-length — the difficulty hits you twice, multiplicatively. That's your "insanely impossible" fish.
3. Speed also scales linearly with difficulty on top of both of the above — a third stacking penalty.
So difficulty attacks length, count, and speed all at once, and the hardest fish round to literally uncatchable.
Proposed number changes (data/constants only, no logic)
A. Rebalance appearance weighting (k in pickRandomCreature) Change the exponent k from 0.5 to 0.75. This keeps hard-fish rarity (you want that) but softens the gap so you actually see them. Hammerhead goes from 1/9.5 odds to roughly 1/5.5 relative to the easiest fish. If you want them rarer, I'd stay nearer 0.6; for more frequent, 0.85.
B. Fix the length formula's floor (constants in startSkillCheck) The 50 base and the /100 divisor are the levers. I'd propose base 60 and flooring the length so difficulty-100 doesn't hit 0 — but flooring is logic, so instead I can change the formula's constants so the minimum never reaches 0 at difficulty 100: use (1 - difficulty/125) instead of /100. At difficulty 100 that's (1 - 0.8) = 0.2 → 12° bars instead of 0. This also fixes the crash without a logic change.
C. Soften bar count difficulty // 10 is steep. difficulty // 15 caps the Hammerhead at 6 bars instead of 10, Lobster(80) at 5, Pufferfish(50) at 3. Much more playable.
D. Creature difficulty spread (optional, in creatures.tsv) Your difficulties cluster at round numbers (10,20,40,50,60,70,80,90,100). That's fine. I'd leave the TSV alone unless you want me to spread them.
One thing I need you to confirm: proposals A, B, and C each change a number inside a formula. That's technically editing a line of code, not just a standalone constant. You said numbers-only and ask before logic. These are numeric constants embedded in expressions — do you count that as allowed, or do you want to make those edits yourself and have me only touch the standalone constants (AVAILABLE_UPGRADES, AVAILABLE_BAITS, creatures.tsv, the k value)?
Tell me which scope you want and I'll proceed.
###############################################
Follow Up:
thats fine to modify - theyre still constants. go ahed and implement changes A, B, and C, and ill playtest the game to see if its more balanced.
Response:
All three done:
A — k: 0.5 → 0.75 (hard fish appear more often while staying rarer than easy ones).
B — length formula: (1 - difficulty/100) * 50 → (1 - difficulty/125) * 60. Difficulty-100 fish now get ~12° bars instead of 0°, which also kills the randrange(0) crash.
C — bar count: difficulty // 10 → difficulty // 15. Hammerhead (100) drops from 10 bars to 6, Lobster (80) to 5, Pufferfish (50) to 3.
Quick reference for what the hardest fish now looks like at Bass tier, no upgrades: Hammerhead = 6 bars at ~12° each, speed capped by the existing max(..., 5). Tough but passable.
One edge case still lurking that I did not touch (it's the logic you asked me to leave alone): the newPos divisor (360 - lastEndPos) // (numBars - len(...)) can still produce randrange(5, 5) if early bars eat most of the circle. Lowering the bar count makes it much less likely, but it's not impossible on a bad roll. Flag it if you hit a crash during playtesting and we can revisit.
Go ahead and playtest — let me know how the frequency and difficulty feel.
############################################### ###############################################
+12 -6
View File
@@ -145,6 +145,7 @@ def onAppStart(app):
app.skillCheckSpeed = 1 app.skillCheckSpeed = 1
app.skillCheckBars = [] # tuples of (position, length) app.skillCheckBars = [] # tuples of (position, length)
app.skillCheckFailed = False app.skillCheckFailed = False
app.showPatternTimer = 30
#FISHING #FISHING
app.fishingTimer = 0 app.fishingTimer = 0
@@ -207,6 +208,7 @@ def resetFishingState(app):
app.skillCheckSpeed = 1 app.skillCheckSpeed = 1
app.skillCheckBars = [] # list of tuples of form (position, length) app.skillCheckBars = [] # list of tuples of form (position, length)
app.skillCheckFailed = False app.skillCheckFailed = False
app.showPatternTimer = 30
app.hookedFish = None app.hookedFish = None
app.fishingTimer = 0 app.fishingTimer = 0
@@ -225,7 +227,7 @@ def pickRandomCreature(app):
print("max difficulty:", max_difficulty) print("max difficulty:", max_difficulty)
#here we take the root of the difficulty to represent the weight of that fish, this makes higher difficulty fish easier to catch once unlocked #here we take the root of the difficulty to represent the weight of that fish, this makes higher difficulty fish easier to catch once unlocked
k = 0.5 k = 0.75
#invert the difficulty and add one so it avoids zero #invert the difficulty and add one so it avoids zero
possibilities = [(creature["name"], (max_difficulty - creature["difficulty"] + 1) ** k) for creature in app.creatures if creature["difficulty"] <= max_difficulty] possibilities = [(creature["name"], (max_difficulty - creature["difficulty"] + 1) ** k) for creature in app.creatures if creature["difficulty"] <= max_difficulty]
print("possibilities:",possibilities) print("possibilities:",possibilities)
@@ -262,14 +264,14 @@ def startSkillCheck(app, creature):
app.skillCheckSpeed = max(difficulty / fishingPower, 5) #this got INCREDIBLY slow with good upgrades app.skillCheckSpeed = max(difficulty / fishingPower, 5) #this got INCREDIBLY slow with good upgrades
numBars = difficulty // 10 - sum([upgrade[2] for upgrade in AVAILABLE_UPGRADES if (upgrade[0] in app.save["owned_upgrades"] and upgrade[1] == "count")]) numBars = difficulty // 15 - sum([upgrade[2] for upgrade in AVAILABLE_UPGRADES if (upgrade[0] in app.save["owned_upgrades"] and upgrade[1] == "count")])
numBars = max(numBars, 1) numBars = max(numBars, 1)
lastPos = 0 lastPos = 0
lastLen = 0 lastLen = 0
while len(app.skillCheckBars) < numBars: while len(app.skillCheckBars) < numBars:
#50 is default length, adjust it proportionally to difficulty, then add upgrade length multipliers. also set to int instead of float #50 is default length, adjust it proportionally to difficulty, then add upgrade length multipliers. also set to int instead of float
newLen = random.randrange(int((1 - (difficulty / 100)) * 50 * math.prod([upgrade[2] for upgrade in AVAILABLE_UPGRADES if (upgrade[0] in app.save["owned_upgrades"] and upgrade[1] == "timing")]))) newLen = random.randrange(int((1 - (difficulty / 125)) * 60 * math.prod([upgrade[2] for upgrade in AVAILABLE_UPGRADES if (upgrade[0] in app.save["owned_upgrades"] and upgrade[1] == "timing")])))
#take the end of the last bar, and add a random amount between 5 degrees #take the end of the last bar, and add a random amount between 5 degrees
#and the remaining amount to the end divided by the number of bars left #and the remaining amount to the end divided by the number of bars left
lastEndPos = (lastPos + lastLen) lastEndPos = (lastPos + lastLen)
@@ -316,9 +318,12 @@ def onStep(app):
app.hookedFish = selectedCreature app.hookedFish = selectedCreature
startSkillCheck(app, selectedCreature) startSkillCheck(app, selectedCreature)
case "skill-check": case "skill-check":
app.skillCheckAngle += app.skillCheckSpeed / 6 #this was far too fast if app.showPatternTimer < 1:
if (app.skillCheckAngle > 360): app.skillCheckAngle += app.skillCheckSpeed / 6 #this was far too fast
finishSkillCheck(app) if (app.skillCheckAngle > 360):
finishSkillCheck(app)
else:
app.showPatternTimer -= 1
def onMousePress(app, mouseX, mouseY): def onMousePress(app, mouseX, mouseY):
@@ -720,6 +725,7 @@ def drawDebug(app):
activeBait: {app.save["current_bait"]} activeBait: {app.save["current_bait"]}
Angle: {rounded(app.skillCheckAngle)} Angle: {rounded(app.skillCheckAngle)}
NextBar: {app.skillCheckBars[0] if len(app.skillCheckBars) > 0 else "N/A"} NextBar: {app.skillCheckBars[0] if len(app.skillCheckBars) > 0 else "N/A"}
showPatternTimer: {rounded(app.showPatternTimer)}
""" """
debugLines = debugStr.splitlines() debugLines = debugStr.splitlines()
size = 16 size = 16
+1 -1
View File
@@ -1 +1 @@
{"cash": 906146.0, "owned_baits": ["Worms", "Prawns", "Tuna", "Bass"], "owned_upgrades": ["Massive Bobber", "Huge Bobber", "Large Bobber", "Medium Bobber", "Galaxy Rod", "Glowing Rod", "Spectral Rod", "Shiny Rod"], "current_bait": "Tuna"} {"cash": 906214.0, "owned_baits": ["Worms", "Prawns", "Tuna", "Bass"], "owned_upgrades": ["Massive Bobber", "Huge Bobber", "Large Bobber", "Medium Bobber", "Galaxy Rod", "Glowing Rod", "Spectral Rod", "Shiny Rod"], "current_bait": "Bass"}

Before

Width:  |  Height:  |  Size: 259 KiB

After

Width:  |  Height:  |  Size: 259 KiB