Day 16 part 1
type
  State = object
    curRoom: string
    time, score: Natural
    valvesOpened: seq[string]
var
  answer: Natural
  options = initTable[string, seq[string]]()  # map all possible ways
  flowRate = initTable[string, int]()  # map room to flow rate
proc evalId(s: State): string = fmt"{s.time}{s.curRoom}"
proc loadObjects() =
  for line in "input.txt".lines:
    var 
      roomName, leadsTo: string
      rate: int
    if scanf(line, "Valve $w has flow rate=$i; tunnels lead to valves $+", roomName, rate, leadsTo):
      options[roomName] = leadsTo.split(", ")
      if rate > 0: flowRate[roomName] = rate  # store only non-zero flow rates
    elif scanf(line, "Valve $w has flow rate=$i; tunnel leads to valve $+", roomName, rate, leadsTo):
      options[roomName] = @[leadsTo]
      if rate > 0: flowRate[roomName] = rate  # store only non-zero flow rates
proc countScore(s: State): Natural =
  for v in s.valvesOpened:
    result.inc flowRate[v]
proc openValve(s: State): State =
  result = State(curRoom: s.curRoom, time: s.time + 1, score: s.score, valvesOpened: s.valvesOpened)
  result.valvesOpened.add s.curRoom
  result.score.inc s.countScore
proc travelTo(s: State, newRoom: string): State =
  result = State(curRoom: newRoom, time: s.time + 1, score: s.score, valvesOpened: s.valvesOpened)
  result.score.inc s.countScore
proc simulate() =
  var 
    states = @[State(curRoom: "AA", time: 1)]
    evaluated = initTable[string, int]()    
  while states.len > 0:
    let state = states.pop
    if state.evalId in evaluated and evaluated[state.evalId] >= state.score: continue
    evaluated[state.evalId] = state.score
    if state.time == 30:  # Time's up.
      answer = max(answer, state.score + state.countScore())
      continue
    if state.curRoom in flowRate and state.curRoom notin state.valvesOpened:
      states.add state.openValve
    for nextRoom in options[state.curRoom]:
      states.add state.travelTo(nextRoom)
loadObjects()  
simulate()Answer is: 1701