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).

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

bjorng
This topic is about Day 17 of the Advent of Code 2020 . Thanks to @egze, we have a private leaderboard: https://adventofcode.com/2020/l...
New
JEG2
Note: This topic is to talk about Day 9 of the Advent of Code. For general discussion about the Advent of Code 2018 and links to topics ...
New
Aetherus
This topic is about the Advent of Code 2021 - Day 4. Thanks to @bjorng , we now have a new Private Leaderboard. The entry code is: 370...
New
New
bjorng
Note: This topic is to talk about Day 9 of the Advent of Code 2019. There is a private leaderboard for elixirforum members. You can join...
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
shritesh
I mapped both the cards and every possible hand to numeric values and sorted them. In part 2 I could only think of replacing the jokers w...
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
bjorng
This topic is about Day 18 of the Advent of Code 2020 . Thanks to @egze, we have a private leaderboard: https://adventofcode.com/2020/l...
New
bjorng
This topic is about Day 6 of the Advent of Code 2021. We have a private leaderboard (shared with users of Erlang Forums ): https://adve...
New

Other popular topics Top

lessless
I believe there are people here who are dealing with CSV files import on the daily basis, and since Excel is a really popular tool there ...
New
grych
Hi folks, Few months ago I have announced the proof-of-concept of the library to manipulate the browsers DOM objects directly from Elixi...
639 49522 488
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
mgjohns61585
Could someone help me? I'm making my first elixir program, number guessing game. I can't figure out how to convert the user's guess from ...
New
WestKeys
Currently suffering from paralysis by [HTTP client] analysis. This is rather unusual in Elixirland as there tends to be consensus on the ...
New
beno
I will often find my self writing things similar to: case some_value do nil -&gt; something() "" -&gt; something() _ -&gt; someth...
New
9mm
I am constructing a JSON object (map) and I need to conditionally set a field. I’m trying to write proper elixir-way code… and I’m at a l...
New
Qqwy
Original source of discussion: This topic on the Pragmatic Programmers' Functional Web Development with Elixir, OTP, and Phoenix forum. ...
New
lucidguppy
I have a super simple question about elixir - how would I take a file like this foo bar baz and output a new file that enumerates th...
New
joeerl
Hello again - after a longish gap I’ve decided I really must dig into Elixir and see what’s been happening here - so I have a few questio...
New

We're in Beta

About us Mission Statement