stevensonmt

stevensonmt

Dynamic Programming algo help (LeetCode - Snakes and Ladders)

Trying to get more facility with dynamic programming concepts on Leetcode and having an issue I can’t find a way around. It’s a chutes and ladders game (that they call snakes and ladders for some reason).
https://leetcode.com/problems/snakes-and-ladders/description/

My intuition for this problem was to start at the end and work backwards since it’s easier to know that the last square before the end will only take one dice roll to get to the end than to know how many it would take to get to the end from the second square. So working backwards goes great except that when I encounter a snake head the value for number of rolls from the snake tail is not known yet. I addressed this by not storing integers but rather functions to get the correct integer value once the entire board has been traversed. This also works great but introduces too much complexity when trying to find the minimum value for a square that is not a ladder bottom or snake head. For those squares I have to assess the next six squares and take the smallest value plus one. I think it’s fair to assume any square that is not a ladder bottom or snake head will not beat out a maximum roll of 6 but not sure. Anyway the trouble I have with this solution below is this error:

eheap_alloc: Cannot allocate 255489152 bytes of memory (of type “heap”). Crash dump is being written to: erl_crash.dump…

EDIT: by changing some Enum calls to Stream calls the above error resolves and now it just times out b/c infinite recursion over a few values (see below).

I’m guessing that all those function calls are piling up recursively? Anyone have suggestions on how to adapt my approach?

defmodule Solution do
  @spec snakes_and_ladders(board :: [[integer]]) :: integer
  def snakes_and_ladders(board) do
    n = length(board)
    last = n * n
    
    dp = do_dp(%{last => fn _ -> 0 end}, flat_board(board, last), last)
    dp[1].(dp) |> IO.inspect()
  end

  @spec flat_board([[integer]], integer) :: map
  defp flat_board(board, last) do 
    
    board
    |> Enum.with_index()
    |> Enum.map(fn {row, i} when rem(i, 2) == 0 -> row
                   {row, i} -> Enum.reverse(row)
                   end)
    |> List.flatten()
    |> Enum.with_index()
    |> Map.new(fn {v,k} -> {last - k, v} end)    
  end  
  
  defp do_dp(seen, _board, last) when last <= 1, do: seen
  defp do_dp(seen, board, last) do 
    1..6
    |> Enum.map(fn i -> last - i end)
    |> Enum.reduce(seen, fn i, sn -> 
        fun = 
          cond do 
          # not a snake head or ladder bottom, just tack on one to last
            board[i] == -1 -> 
              fn cache -> 
                 next =
                   1..6
                   |> Stream.filter(fn j -> Map.get(board, i + j, -1) != -1 end)
                   |> Stream.map(fn j -> cache[i + j].(cache) end)
                   |> Stream.concat([cache[last].(cache)])
                                  
                Enum.min(next) 
                |> Kernel.+(1)
              end
          # bottom of ladder, add one to distance to end from top of ladder
            Map.get(sn, board[i], false) -> fn cache -> cache[board[i]].(cache) end
          # head of snake, add one to distance from tail of snake but this not calculated yet.
            board[i] < i -> fn cache -> cache[board[i]].(cache) end
          # should only be reachable when i < 1 b/c board[i] does not exist
            true -> fn _ -> 0 end
          end
        Map.put(sn, i, fun)  
    end)
    |> do_dp(board, last - 6)
  end
end

If I inspect the case where a space is not a snake head or ladder bottom I see that it ends up looping over these conditions infinitely:

last=36
i=30
: 0
last=30
i=24
: 1
last=24
i=18
: 2
last=36
i=35
: 0

I’m an idiot. I’m decrementing over 1..6 and then incrementing each value over 1..6 in the same loop so of course it’s going to infinity.

Most Liked

stevensonmt

stevensonmt

Ah. I figured it was something like that, but conceptually chutes makes a lot more sense than snakes as a contrast to ladders.

Where Next?

Popular in Challenges Top

maennchen
Ok, that was a rough one today. I haven’t found a way to improve the algorithm further. Part 1 runs in .5 seconds, Part 2 in ~ 5 minutes...
New
rugyoga
Not the prettiest but it works https://github.com/rugyoga/aoc2023/blob/main/lib/2024/9.ex
New
Aetherus
This topic is about Day 15 of the Advent of Code 2020 . Thanks to @egze, we have a private leaderboard: https://adventofcode.com/2020/l...
New
bjorng
Here is my solution for day 1 of Advent of Code: defmodule Day01 do def part1(input) do all = parse(input) {first, second} = E...
New
New
bjorng
Note: This topic is to talk about Day 3 of the Advent of Code 2019 . There is a private leaderboard for elixirforum members. You can jo...
New
bjorng
This topic is about Day 1 of the Advent of Code 2021. We have a private leaderboard (shared with users of Erlang Forums): https://adven...
New
stevensonmt
Anyone else think the prompt for this challenge is contradictory? The rules for comparing packets include If both values are lists, c...
New
rvnash
Anyone have a solution to Part 2 today? Part 1 was straight forward, but I can’t figure out a programatic way to do part 2. I understand ...
New
bjorng
Note: This topic is to talk about Day 23 of the Advent of Code. For general discussion about the Advent of Code 2018 and links to topics...
New

Other popular topics Top

malloryerik
Hi, this is for people who, like me, have had some friction using .html.heex templates in VSCode. The solution seems to be, in a hyphena...
New
mcarvalho
What is the difference between System.get_env and Application.get_env? For example, what are best practices to use one versus another.
New
chrismccord
As promised, the first release candidate of Phoenix 1.3.0 is out! This release focuses on code generators with improved project structure...
New
Patoshizzle
After calling mix ecto.create I get this error: 17:00:32.162 [error] GenServer #PID&lt;0.412.0&gt; terminating ** (Postgrex.Error) FATAL...
New
jononomo
I am trying to figure out how Mix knows whether the environment is test, dev, or prod – where is this set? Thanks.
New
JakeBecker
TL;DR: I’ve just released an implementation of Microsoft’s IDE-independent Language Server Protocol for Elixir. It adds language support ...
1144 53690 245
New
stefanluptak
Hello everybody, usually, I use a 29" ultra-wide monitor for VSCode which can easily accomodate explorer (files panel) + file with code ...
New
Qqwy
Original source of discussion: This topic on the Pragmatic Programmers’ Functional Web Development with Elixir, OTP, and Phoenix forum. ...
New
KronicDeth
Elixir plugin for JetBrain’s IntelliJ Platform (including Rubymine) This is a plugin that adds support for Elixir to JetBrains IntelliJ...
289 36128 110
New
marick
I had some trouble figuring out how to make many-to-many associations work. Once I got it working, I wrote a blog post. Because I’m a nov...
New

We're in Beta

About us Mission Statement