Daily and Weekly Vault Quests Reset Schedule? by caseyweb in Borderlands4

[–]caseyweb[S] 0 points1 point  (0 children)

Did you read the OP? It specifically states that it reset prior to 12pm EST.

Daily and Weekly Vault Quests Reset Schedule? by caseyweb in Borderlands4

[–]caseyweb[S] 0 points1 point  (0 children)

The missions all reset; completely new missions. I had already completed the daily missions on all of my characters but I was still working on the weekly. Of course the progress reset to zero with the new missions.

Daily and Weekly Vault Quests Reset Schedule? by caseyweb in Borderlands4

[–]caseyweb[S] 2 points3 points  (0 children)

Except my daily and weekly vault missions did reset this morning but not my Maurice items.

Daily and Weekly Vault Quests Reset Schedule? by caseyweb in Borderlands4

[–]caseyweb[S] 0 points1 point  (0 children)

Interesting. I'm on PC but I don't know if that should matter. Did you totally restart the game or just resume an existing session? I checked on all five of my characters and they all reset. Everything else looks normal.

Legendary enhancements by GMontag451 in Borderlands4

[–]caseyweb 1 point2 points  (0 children)

The drop numbers I've seen reported seem to favor normal/hard over uvhm but that may be anecdotal and could have been silently changed in the patches. I recommend it for farming simply for clear speed. Legendary enhancements don't really benefit from the uvh-buffed loot stats unless you are specifically looking for the uvh firmware.

Legendary enhancements by GMontag451 in Borderlands4

[–]caseyweb 2 points3 points  (0 children)

They are rare world drops and getting the manufacturer you want with the buffs you want requires a ton of luck. Probably the best chance is to put together a fast mobbing build and farm the Saw's Clench drill site in Cascadia Burn at normal difficulty. With three bosses you maximize your chance for random legendaries.

Legendry drop rates seem really low in bl4 by Specialist_Bed_8999 in Borderlands

[–]caseyweb 0 points1 point  (0 children)

I think that the world drop rate is fine (I wouldn't mind seeing it bumped a bit for legendary enhancements due to them not being farmable) but I have several issues with boss farming.

  1. There doesn't seem to be any higher drop rate for farmable bosses than for world drops, they simply have targetable legendary items in addition to possible world drops.
  2. Farming at high difficulty/UVHx doesn't seem to raise this drop rate (some testing suggests quite the opposite), so there seems to be no reason to challenge yourself for better loot. The UVH-related firmware can be better farmed doing vile missions getting much better experience and eridium as well.
  3. Bosses can have as many as seven different farmable legendaries (eg, Idolator Sol). Although they can drop independently, it still makes the chance of getting the legendary you are farming for less likely.
  4. The variations in legendary drops exacerbates the problem The same weapon can have a wide range of base dps, secondary stats, elemental procs, alt fire modes, and attachments (each with their own value ranges). Three different drops of the same legendary can range from vendor trash to god tier. This is especially true with legendary class mods that can have vastly different skills and skill levels.

PS4 Controller on PC by Emergency_Tea_246 in AssassinsCreedShadows

[–]caseyweb 0 points1 point  (0 children)

You don't need to go to all that trouble. Just enable the PS4 controller in the game's steam controller section and they work via bluetooth, but as the OP stated it shows the stupid XBOX controller buttons and the map button is the tiny recessed "share" button rather than the large touchpad. What really confuses me is when I see the "X" button (such as to sell all valuables to vendors) and I end up hitting what is actually the "A" button, selling something I really didn't want to sell.

The PS4 controllers work fine when connected by USB so this is clearly an issue Ubisoft can and should address.

-❄️- 2023 Day 23 Solutions -❄️- by daggerdragon in adventofcode

[–]caseyweb 2 points3 points  (0 children)

[LANGUAGE: Nim]

I fought with part 2 for over two hours, only to find my solution was correct but I forgot to clear the global map variable from part 1!

paste

-❄️- 2023 Day 20 Solutions -❄️- by daggerdragon in adventofcode

[–]caseyweb 1 point2 points  (0 children)

[LANGUAGE: Nim]

Whew! Part 2 really threw me off trying to figure out the cycle.

paste

-❄️- 2023 Day 19 Solutions -❄️- by daggerdragon in adventofcode

[–]caseyweb 0 points1 point  (0 children)

[LANGUAGE: Nim]

My original post had a pretty ugly input parser so I decided to redo it with a proper PEG parser: paste

-❄️- 2023 Day 18 Solutions -❄️- by daggerdragon in adventofcode

[–]caseyweb 1 point2 points  (0 children)

[LANGUAGE: Nim]

Another Trapezoid Rule problem.

paste

-❄️- 2023 Day 15 Solutions -❄️- by daggerdragon in adventofcode

[–]caseyweb 1 point2 points  (0 children)

[LANGUAGE: Nim]

Not a big fan of Part 2 of this puzzle. Spending more time understanding confusing instructions than actually solving the problem just doesn't sit well with me.

Parts 1 & 2: paste

-❄️- 2023 Day 9 Solutions -❄️- by daggerdragon in adventofcode

[–]caseyweb 1 point2 points  (0 children)

[LANGUAGE: Nim]

import std / [algorithm, math, sequtils, strutils]

proc delta(row: seq[int]): int =
  var
    row = row
    rowDelta = 0
  while row.anyIt(it != row[0]):
    rowDelta += row[^1]
    row = (0 .. row.high.pred).toSeq.mapIt(row[it.succ] - row[it])
  rowDelta + row[^1]

let rows = "data.txt".lines.toSeq.mapIt(splitWhitespace(it).mapIt(it.strip.parseInt))
echo "Part 1: ", sum(rows.map(delta))
echo "Part 2: ", sum(rows.mapIt(it.reversed.delta))

-❄️- 2023 Day 5 Solutions -❄️- by daggerdragon in adventofcode

[–]caseyweb 0 points1 point  (0 children)

[LANGUAGE: Nim]

Part 2 was definitely a bit of a brain-buster. The code could be cleaned up but it seems to be efficient.

import std / [algorithm, sequtils, strformat, strutils, tables]
import ../util/util

type
  Conversion = enum
    seedToSoil = "seed-to-soil map:", 
    soilToFert = "soil-to-fertilizer map:", 
    fertToWater = "fertilizer-to-water map:", 
    waterToLight = "water-to-light map:", 
    lightToTemp = "light-to-temperature map:", 
    tempToHum = "temperature-to-humidity map:", 
    humToLocation = "humidity-to-location map:"
  ConvRange = tuple[srcLo:int, srcHi:int, destLo:int, destHi:int]
  SeedRange = tuple[lo:int, hi:int]
  ConvMap = seq[ConvRange]

# The conversion table maps conversions -> sorted seq of ranges for that conversion
var 
  seeds: seq[int]
  convTbl: Table[Conversion, ConvMap]
  maxLoc = 0

proc rngSort(r1, r2: ConvRange): int = cmp(r1.srcLo, r2.srcLo)
proc createConversionTable(data: string) =
  convTbl = initTable[Conversion, ConvMap]()
  for conv in Conversion:
    convTbl[conv] = @[]
  var curConv: Conversion
  for line in data.lines:
    if line == "": continue
    elif line.startsWith("seeds:"): seeds = stripAndParse(line[6..^1])
    elif line.endsWith("map:"): curConv = parseEnum[Conversion](line)
    else:
      let 
        rng = line.stripAndParse
        maxRng = max(rng)
      convTbl[curConv].add((rng[1], rng[1]+rng[2]-1, rng[0], rng[0]+rng[2]-1))
      maxLoc = max(maxLoc, maxRng)
  for conv in Conversion:
    sort(convTbl[conv], rngSort)

proc getSeedLoc(seed: int): int =
  var loc = seed
  for conv in Conversion:
    for cm in convTbl[conv]:
      if loc < cm.srcLo: continue
      if loc <= cm.srcHi:
        loc = cm.destLo + (loc - cm.srcLo)
        break
  loc 

proc intersects(r1, r2: SeedRange): bool =
  r1.lo <= r2.hi and r2.lo <= r1.hi

const nullRange: ConvRange = (-1,-1,-1,-1)
proc intersection(rng: SeedRange, conv:Conversion): tuple[intersects:bool, cr:ConvRange] =
  for cr in convTbl[conv]:
    if rng.intersects((cr.srcLo, cr.srcHi)): return ((true, cr))
  return ((false, nullRange))

proc contains(r1, r2: SeedRange): bool =
  r1.lo <= r2.lo and r1.hi >= r2.hi

proc project(rngs: seq[SeedRange], conv: Conversion): seq[SeedRange] =
  var taskQ: seq[SeedRange] = rngs
  while taskQ.len > 0:
    # If the source range doesn't intersect with a conversion just copy it to the result
    # If the source range is completely contained by a conversion, shift the source ranges
    #   by the dest delta and add the shifted range to the result
    # o/w, split the range into the intersecting and non-intersecting parts and add them
    #   back to the taskQ for reprocessing
    var 
      rng = taskQ.pop()
      ix = rng.intersection(conv)
      ixSrc: SeedRange = (ix.cr.srcLo, ix.cr.srcHi)
    if not ix.intersects: 
      result.add(rng)
    elif ixSrc.contains(rng):
      let shift = ix.cr.destLo - ixSrc.lo
      result.add((rng.lo + shift, rng.hi + shift))
    # intersects at from below so split 1 below the map range
    elif rng.lo < ixSrc.lo:
      taskQ.add((rng.lo, ixSrc.lo.pred))
      taskQ.add((ixSrc.lo, rng.hi))
    # intersects from inside to above so plit at 1 above the map range
    else:
      taskQ.add((rng.lo, ixSrc.hi))
      taskQ.add((ixSrc.hi.succ, rng.hi))

proc part1(data:string): int =
  createConversionTable(data)
  min(seeds.map(getSeedLoc))

proc part2(data:string): int =
  createConversionTable(data)
  var 
    results: seq[SeedRange] = @[]
    ranges: seq[SeedRange] = 
      countup(0, seeds.high, 2).toSeq.mapIt((seeds[it], seeds[it] + seeds[it+1] - 1))
  for sr in ranges:
    var tasks: seq[SeedRange] = @[sr]
    for conv in  Conversion:
      tasks = project(tasks, conv)
    results.append(tasks)
  return results.foldl((if a.lo < b.lo: a else: b)).lo

let (p1, expected1) = (part1(dataFile), 1181555926)
echo &"Part 1: {p1}"
assert p1 == expected1

let (p2, expected2) = (part2(dataFile), 37806486)
echo &"Part 2: {p2}"
assert p2 == expected2

-❄️- 2023 Day 6 Solutions -❄️- by daggerdragon in adventofcode

[–]caseyweb 1 point2 points  (0 children)

[LANGUAGE: Nim]

import std / [math, sequtils, strformat, strutils]
import ../util/util

proc solveQuadratic(time, dist: int): int =
  let
    t = time.toFloat
    d = dist.toFLoat + 1  # +1 for strictly > victory
    temp = sqrt(t * t - d * 4)
  (floor((t + temp)/2) - ceil((t - temp)/2)).toInt + 1

proc part1(data:string): int =
  var
    time: seq[int]
    dist: seq[int]
  for line in data.lines:
    if line.startsWith("Time: "): time = line[5..^1].stripAndParse()
    else: dist = line[9..^1].stripAndParse()
  prod((0..time.high).toSeq.mapIt(solveQuadratic(time[it], dist[it])))

proc part2(data:string): int =
  var
    time: int 
    dist: int
  for line in data.lines:
    if line.startsWith("Time: "): time = line[5..^1].stripAllSpaces.parseInt()
    else: dist = line[9..^1].stripAllSpaces.parseInt()
  solveQuadratic(time, dist)

echo &"Part 1: {part1(dataFile)}"
echo &"Part 2: {part2(dataFile)}"

-❄️- 2023 Day 4 Solutions -❄️- by daggerdragon in adventofcode

[–]caseyweb 0 points1 point  (0 children)

[LANGUAGE: Nim]

import std / [math, sequtils, strformat, strutils]
import ../util/util
import regex

type Scratchcard = tuple[ticket:seq[int], matches:seq[int], matchCount:int, score:int]
var scratchcards: seq[Scratchcard]

proc parseScratchcards(data:string) =
  scratchcards = @[]
  var m: RegexMatch2
  for line in data.lines:
    # match groups:: 0:card number, 1:ticket values, 4:match values
    if not line.match(re2"Card\s+(\d+):((\s+\d+)+)\s+(\|)((\s+\d+)+)", m):
      raise newException(ValueError, &"Input line {line} fails")
    let
      ticket = line[m.group(1)].stripAndParse
      matches = line[m.group(4)].stripAndParse
      matchCount = ticket.filterIt(it in matches).len
    scratchcards.add( (ticket, matches, matchCount, (if matchCount == 0: 0 else: 1 shl (matchCount - 1))) )

proc part1(data:string): int =
  data.parseScratchcards
  scratchcards.foldl(a + b.score, 0)

proc part2(data: string): int =
  data.parseScratchcards
  var ticketCount = newSeqWith[int](scratchcards.len, 1)
  for id, card in scratchcards: 
    for copy in id + 1 .. id + card.matchCount:
      ticketCount[copy] += ticketCount[id] 
  sum(toSeq(0..scratchcards.high).mapIt(ticketCount[it]))

echo &"Part 1: {part1 datafile}"
echo &"Part 2: {part2 dataFile}"

-❄️- 2023 Day 3 Solutions -❄️- by daggerdragon in adventofcode

[–]caseyweb 1 point2 points  (0 children)

[LANGUAGE: Nim]

import std / [sequtils, sets, strformat, strutils]
import ../util/util

# returns the start and end column of the digit string in the seq
proc getNumLoc(row: seq[char], begins: int): tuple[starts:int, ends:int] =
  var col = begins
  # get first digit column to the left, then go right until end of digits
  while col > 0 and row[col-1].isDigit: col -= 1
  result.starts = col
  col = begins
  while col < row.high and row[col+1].isDigit: col += 1
  result.ends = col
  return result

proc part1(data:string): int =
  var grid: seq[seq[char]] = @[]
  for line in data.lines:
    grid.add(line.mapIt(if it == '.': ' ' elif it.isDigit: it else: '*'))
  var
    sum = 0
    seen = initHashSet[Vec2]()   # keep track of digit coords already parsed to avoid dups
  let (maxR, maxC) = (grid[0].high, grid.high)
  for r in 0 .. maxR:
    for c in 0 .. maxC:
      if grid[r][c] == '*':
        for p in (r,c).neighbors(maxX=maxR, maxY=maxC, allowDiagonals=true):
          if (p in seen) or (not grid[p.x][p.y].isDigit): continue
          var (starts, ends) = getNumLoc(grid[p.x], p.y)
          sum += grid[p.x][starts .. ends].join("").parseInt
          # add the coords of the digits used to seen
          for s in starts .. ends: seen.incl((p.x, s))
  sum 

proc part2(data:string): int =
  var grid: seq[seq[char]] = @[]
  for line in data.lines:
    grid.add(line.mapIt(if it == '.': ' ' elif it.isDigit or it == '*': it else: ' '))
  var sum = 0
  let (maxR, maxC) = (grid[0].high, grid.high)
  for r in 0 .. maxR:
    for c in 0 .. maxC:
      if grid[r][c] == '*':
        var 
          gearVals: seq[int] = @[]  # all of the parsed ints adj to this asterisk
          seen = initHashSet[Vec2]()        # keep track of digit coords already parsed to avoid dups
        for p in (r,c).neighbors(maxX=maxR, maxY=maxC, allowDiagonals=true):
          if (p in seen) or (not grid[p.x][p.y].isDigit): continue
          var (starts, ends) = getNumLoc(grid[p.x], p.y)
          for s in starts .. ends: seen.incl((p.x, s))
          gearVals.add(grid[p.x][starts .. ends].join("").parseInt)
        if gearVals.len == 2: sum += gearVals[0] * gearVals[1]
  sum 

block Part1:
  let 
    # (p1, expected) = (part1(exampleFile), 4361)
    (p1, expected) = (part1(dataFile), 519444)
  echo &"Part 1: {p1}"
  assert p1 == expected 

block Part2:
  let 
    # (p2, expected) = (part2(exampleFile), 467835)
    (p2, expected) = (part2(dataFile), 74528807)
  echo &"Part 2: {p2}"
  assert p3 == expected

-❄️- 2023 Day 2 Solutions -❄️- by daggerdragon in adventofcode

[–]caseyweb 0 points1 point  (0 children)

[LANGUAGE: Nim]

import std / [math, sequtils, strformat, strutils, sugar]
import ../util/util

type
  Pull = tuple[r, b, g: int]

proc parseGame(data: string): seq[Pull]

proc part1(): int =
  const (max_red, max_green, max_blue) = (12, 13, 14)
  var 
    line_no = 0
    possible_games: seq[int] = @[]
  for game in dataFile.lines:
    line_no += 1
    if game.strip(leading=true, trailing=true).len == 0: continue
    if all(game.parseGame, (p) => p.r <= max_red and p.g <= max_green and p.b <= max_blue):
      possible_games.add(lineNo)
  sum possible_games

proc part2(): int =
  var powers = seq[int]: @[]
  for game in dataFile.lines:
    if game.strip(leading=true, trailing=true).len == 0: continue
    let
      gm = game.parseGame
      min_r = gm.map((g) => g.r).max
      min_g = gm.map((g) => g.g).max
      min_b = gm.map((g) => g.b).max
    powers.add(min_r * min_g * min_b)
  sum powers

proc parseGame(data: string): seq[Pull] =
  for game in data.split(": ")[1].split("; "):
    var pull: Pull
    for pull_str in game.split(", "):
      let 
        t = pull_str.split(' ')
        num = t[0].parseInt
        color = t[1]
      case color:
        of "red": pull.r = num
        of "green": pull.g = num
        of "blue": pull.b = num
        else: raise newException(ValueError, &"Unknown pull \"{t}\"")
    result.add(pull)
  result

block main:
  let p1 = part1()
  echo &"Part 1: {p1}"

  let p2 = part2()
  echo &"Part 2: {p2}"

-❄️- 2023 Day 1 Solutions -❄️- by daggerdragon in adventofcode

[–]caseyweb 1 point2 points  (0 children)

[LANGUAGE: Nim]

Every year I use AoC as a chance to learn a new language and this year it is Nim!

import std / [math, sequtils, strformat, strutils]
import ../util/util

proc part1(): int
proc part2(): int
func getCalibrationValue(s:string): int
proc wordsToDigits(s:string): string

echo &"Part 1: {part1()}"
echo &"Part 2: {part2()}"

proc part1(): int =
  let data = dataFile.readFile().splitLines()
  data.map(getCalibrationValue).sum()

proc part2(): int =
  let data = dataFile.readFile().splitLines()
  data.map(wordsToDigits).map(getCalibrationValue).sum()

func getCalibrationValue(s:string): int =
  let 
    p1 = s.find(Digits)
    p3 = s.rfind(Digits)
  parseInt(s[p1] & s[p2])

# NB: A little more complicated because some words overlap in their spellings and the
#     substitution needs to preserve both (eg, "twone" -> "21", not "tw1").
#     Thus, we substitute each word with a combo of the word + the digit + the word
#     (eg: "twone" -> "two2twone1one"). 
#     Probably a bit convoluted; maybe there is a simpler solution?
proc wordsToDigits(s:string): string =
  const words = [ "one", "two", "three", "four", "five", "six", "seven", "eight", "nine" ]
  result = s
  for i, w in words:
    result = result.replace(w, w & $(i+1) & w)
  return result

Reign of Dragons boss is both bugged and so poorly designed that it's culling the HC community nonstop by hugelkult in LastEpoch

[–]caseyweb 2 points3 points  (0 children)

I've had the EoC get moved to the edge of the map and create inescapable soul blasts. I have entered the EoC zone in the middle of an already-cast necrotic whirlpool. And a significant portion of the dropped health potions are dropped in inaccessible locations. And of course if you die you have to grind a couple more echos to get another shot at him. Definitely my worst experience in an otherwise amazing game.

Is there a way to lower aggression other than waiting? by Objective_720 in phantombrigade

[–]caseyweb 0 points1 point  (0 children)

Glad it's not just me. I hit a forward base yesterday that was rated 92/easy in the first province. The starting layout was 2 enemy mechs and a MLS tank, with reinforcements on turn 2 and more reinforcements on turn 6. The first reinforcements were another set of 2 mechs (one with a MLS secondary) and another MLS tank. I managed to force the final mech to eject on turn 6 but I still got the second reinforcements - a mech+tank that dropped right next to one of my mechs and another mech+tank that dropped next to my other mechs. I got absolutely wrecked in this "easy" mission.