dankgrinder 7.11.0 → 7.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1409,6 +1409,7 @@ async function runFarm({ channel, waitForDankMemer, client, redis, accountId, fo
1409
1409
  let forcedNextAction = null; // When advancing a phase, force the next action check
1410
1410
  let lastApplyResp = null; // Track the last apply response for coin/cooldown parsing
1411
1411
  let lastRejectedAction = null; // Track last rejected action to break empty-farm loops
1412
+ let lastRejectedAction2 = null; // Track previous rejected action to detect 3-in-a-row loops
1412
1413
 
1413
1414
  while (cycleDepth < 5) {
1414
1415
  // Reset per-cycle state
@@ -1589,6 +1590,30 @@ async function runFarm({ channel, waitForDankMemer, client, redis, accountId, fo
1589
1590
  const ephem = lastApplyResp?._capturedEphemeral;
1590
1591
  const ephemText = (ephem?.cv2Text || ephem?.allText || '').toLowerCase();
1591
1592
 
1593
+ // Generic "nothing to X" no-op: when an action completes but has nothing to act on,
1594
+ // cascade forward without treating it as a rejection. Also use this as a safety break
1595
+ // when the same action keeps getting rejected (stuck in a loop).
1596
+ if (
1597
+ // "Nothing to water/hoe/plant/harvest" — action succeeded but was a no-op
1598
+ /nothing to (water|hoe|plant|harvest)|no crops? (to |to )?(water|harvest)|0 x [a-z]|nothing to do/i.test(ephemText) ||
1599
+ // Safety: if the same action was rejected 3+ cycles in a row, something is stuck — break
1600
+ (lastRejectedAction === action && lastRejectedAction2 === action)
1601
+ ) {
1602
+ LOG.warn(`[farm:cycle:${cycleDepth}:reject] ${action} is a no-op or stuck loop (${ephemText.slice(0, 80)}) — cascading to next phase`);
1603
+ const currentIdx = FARM_PHASE_ORDER.indexOf(action);
1604
+ const nextPhase = FARM_PHASE_ORDER[currentIdx + 1] || null;
1605
+ lastRejectedAction2 = lastRejectedAction;
1606
+ lastRejectedAction = action;
1607
+ forcedNextAction = nextPhase;
1608
+ lastAction = action;
1609
+ justRejected = true;
1610
+ cycleDepth++;
1611
+ if (!nextPhase) { forcedNextAction = null; break; }
1612
+ await reenterManage();
1613
+ await sleep(300);
1614
+ continue;
1615
+ }
1616
+
1592
1617
  // Helper: re-enter the manage menu from the farm view so the next
1593
1618
  // findNextFarmActionFromManage iteration has buttons to work with.
1594
1619
  async function reenterManage() {
@@ -1614,6 +1639,8 @@ async function runFarm({ channel, waitForDankMemer, client, redis, accountId, fo
1614
1639
  LOG.warn(`[farm:cycle:${cycleDepth}:reject] Hoe rejected: tiles not empty — cascading to water`);
1615
1640
  const currentIdx = FARM_PHASE_ORDER.indexOf(action);
1616
1641
  const nextPhase = FARM_PHASE_ORDER[currentIdx + 1] || null;
1642
+ lastRejectedAction2 = lastRejectedAction;
1643
+ lastRejectedAction = action;
1617
1644
  forcedNextAction = nextPhase;
1618
1645
  lastAction = action;
1619
1646
  justRejected = true;
@@ -1630,11 +1657,13 @@ async function runFarm({ channel, waitForDankMemer, client, redis, accountId, fo
1630
1657
  LOG.warn(`[farm:cycle:${cycleDepth}:reject] Water rejected (${ephemText.slice(0, 100)}) — cascading to plant`);
1631
1658
  const currentIdx = FARM_PHASE_ORDER.indexOf(action);
1632
1659
  const nextPhase = FARM_PHASE_ORDER[currentIdx + 1] || null;
1660
+ lastRejectedAction2 = lastRejectedAction;
1661
+ lastRejectedAction = action;
1633
1662
  forcedNextAction = nextPhase;
1634
1663
  lastAction = action;
1635
1664
  justRejected = true;
1636
1665
  // Break oscillation guard: if we bounced back here from plant, stop.
1637
- if (lastRejectedAction === 'plant' && action === 'water') {
1666
+ if (lastRejectedAction2 === 'plant' && lastRejectedAction === 'water') {
1638
1667
  LOG.warn(`[farm:cycle:${cycleDepth}:reject] hoe→water→plant oscillation detected — breaking`);
1639
1668
  break;
1640
1669
  }
@@ -1651,6 +1680,8 @@ async function runFarm({ channel, waitForDankMemer, client, redis, accountId, fo
1651
1680
  LOG.warn(`[farm:cycle:${cycleDepth}:reject] Plant rejected (${ephemText.slice(0, 100)}) — cascading to harvest`);
1652
1681
  const currentIdx = FARM_PHASE_ORDER.indexOf(action);
1653
1682
  const nextPhase = FARM_PHASE_ORDER[currentIdx + 1] || null;
1683
+ lastRejectedAction2 = lastRejectedAction;
1684
+ lastRejectedAction = action;
1654
1685
  forcedNextAction = nextPhase;
1655
1686
  lastAction = action;
1656
1687
  justRejected = true;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dankgrinder",
3
- "version": "7.11.0",
3
+ "version": "7.12.0",
4
4
  "description": "Dank Memer automation engine — grind coins while you sleep",
5
5
  "bin": {
6
6
  "dankgrinder": "bin/dankgrinder.js"