5

I am trying to write a recursive function that will take a list containing a list of integers as an input and return a tuple of type ([Int],Int). ([Int],Int)

This is for a "board game" where you are supplied with a board:

 [[5,4,3,8,6],
  [0,2,1,0,7],
  [0,1,9,4,3],
  [2,3,4,0,9]]

This would be a board with 4 rows and 5 columns. The numbers inside the list are "coin values". The objective of this board game would be to go from the top of the list to the bottom collecting the coins. You are able to start in any position from the top row and to move down, you can go straight down, or diagonally to left or right. You would want the path that will give you the largest total coin values.

I've created a first function where you input a list of paths [([Int],Int)] and it returns the path ([Int],Int) with maximum coin value.

Now I need to create a function to actually generate the list of paths that I will input into the first function.

I know that I will have to use recursion. I will input the board (like one above) and a starting column. I will have to take the column number and then create a list of all possible paths. If I start with a column number, my next possible steps are positions (in the next row)- same column number, column num -1 and column num +1. I would need to recursively call this until I reach the bottom.

How would I be able to store these path steps as I go and then store the final - list of all possible paths?

([Int],Int) - [Int] is the position in list / column numbers or the rows and the Int is the coin value.

I'm new to haskell and while I understand what I have to do, it's really difficult to write the code.

  • Do these have to be straight lines or can you decide a direction after each move? – Adam Gordon Bell Feb 02 '13 at 21:58
  • you need to create all possible path so if I start at row 1, position 2, then I could go to row 2, position 1, 2, 3 and then from then on, for each possible position in row 2, you can go straight down, diagonally left and right. – user2035972 Feb 02 '13 at 22:00
  • If it helps, this is what it would look like visually:http://postimage.org/image/yrnrv8y2p/ – user2035972 Feb 02 '13 at 22:03
  • I suggest that you start by describing in words what the path generating function does. How do you determine the list and Int in each pair? – Code-Apprentice Feb 02 '13 at 22:19
  • 1
    Take a look at a [knights journey](http://learnyouahaskell.com/a-fistful-of-monads#the-list-monad) . You can adapt it to give you all paths and then use the paths to find the max path, using some sort of lookup. – Adam Gordon Bell Feb 02 '13 at 22:24
  • Just a comment really, but have you ever heard of [dynamic programming](http://en.wikipedia.org/wiki/Dynamic_programming)? It may lead you to a better solution than the one you're currently thinking of. – Chris Taylor Feb 02 '13 at 23:29
  • You may find my [grid](http://hackage.haskell.org/package/grid-2.1.1) ([userguide](https://github.com/mhwombat/grid/wiki)) package helpful. In particular, see the GridMap class. – mhwombat Feb 07 '13 at 10:07
  • FWIW, it is a bit funny that you emphasize it is a *recursive* function. Since Haskell does not have loops, one of the following holds for all functions a) it is trivial or some closed formula, b) it uses higher order functions or abstractions that hide the recursivity or c) it is recursive. – Ingo Feb 07 '13 at 10:58

3 Answers3

1

You don't "store" intermediate values in some variable in idiomatic functional code. Rather, you keep them as an accumulating parameter which you pass along using a function such as foldr.

http://hackage.haskell.org/packages/archive/base/latest/doc/html/Prelude.html#v:foldr

sclv
  • 38,665
  • 7
  • 99
  • 204
0

I guess I am now in a position to (easily) adapt my answer for another question to this one. I listed the allowed index combinations and mapped the board to them. (pat's comment helped me improve index_combinations)

*Main> :load "new1.hs"
[1 of 1] Compiling Main ( new1.hs, interpreted )
Ok, modules loaded: Main.
*Main> result
([8,7,4,9],28)
*Main> path
[3,4,3,4]

import Data.List
import Data.Ord
import Data.Maybe

r = [[5,4,3,8,6],
     [0,2,1,0,7],
     [0,1,9,4,3],
     [2,3,4,0,9]]

r1 = r !! 0
r2 = r !! 1
r3 = r !! 2
r4 = r !! 3

index_combinations = 
  [[a,b,c,d] | a <- [0..4], b <- [max 0 (a-1)..min 4 (a+1)], 
   c <- [max 0 (b-1)..min 4 (b+1)], d <- [max 0 (c-1)..min 4 (c+1)]]  

mapR xs = [r1 !! (xs !! 0), r2 !! (xs !! 1), 
           r3 !! (xs !! 2), r4 !! (xs !! 3)]

r_combinations = map mapR index_combinations
r_combinations_summed = zip r_combinations $ map (foldr (+) 0) r_combinations

result = maximumBy (comparing snd) r_combinations_summed
path = index_combinations !! fromJust (elemIndex result r_combinations_summed)
Community
  • 1
  • 1
גלעד ברקן
  • 23,602
  • 3
  • 25
  • 61
0

If you're interested in using my package grid (userguide) here as an example to get you started. (And if you don't want to use it, you may find some of the source code helpful.)

Create a grid with 4 rows and 5 columns.

λ> :m + Math.Geometry.Grid
λ> let g = rectSquareGrid 4 5
λ> indices g
[(0,0),(0,1),(0,2),(0,3),(1,0),(1,1),(1,2),(1,3),(2,0),(2,1),(2,2),(2,3),(3,0),(3,1),(3,2),(3,3),(4,0),(4,1),(4,2),(4,3)]

We want to be able to map "coin values" to grid positions, so we'll create a GridMap.

λ> :m + Math.Geometry.GridMap
λ> let m = lazyGridMap g [5,4,3,8,6,0,2,1,0,7,0,1,9,4,3,2,3,4,0,9]
λ> m
lazyGridMap (rectSquareGrid 4 5) [5,4,3,8,6,0,2,1,0,7,0,1,9,4,3,2,3,4,0,9]
λ> toList m
[((0,0),5),((0,1),4),((0,2),3),((0,3),8),((1,0),6),((1,1),0),((1,2),2),((1,3),1),((2,0),0),((2,1),7),((2,2),0),((2,3),1),((3,0),9),((3,1),4),((3,2),3),((3,3),2),((4,0),3),((4,1),4),((4,2),0),((4,3),9)]

We can find out the neighbours of any cell in the grid, but for your application, we run into a bit of a problem: my RectSquareGrid type doesn't allow diagonal moves.

λ> neighbours (1,2) m
[(0,2),(1,3),(2,2),(1,1)]

Now, I'd be happy to create a new type of Grid that would meet your needs. Alternatively, you could write your own function which would include diagonal neighbours:

λ> let neighbours2 (x, y) g = filter (`inGrid` g) [(x-1,y-1), (x-1,y), (x-1,y+1), (x,y-1), (x,y+1), (x+1,y-1), (x+1,y), (x+1,y+1)]
λ> neighbours2 (1,2) m
[(0,1),(0,2),(0,3),(1,1),(1,3),(2,1),(2,2),(2,3)]

But you're only interested in allowing downward moves, either straight down or diagonal, so here's a more useful function:

λ> let allowedMoves (x, y) g = filter (`inGrid` g) [(x+1,y-1), (x+1,y), (x+1,y+1)]
λ> allowedMoves (1,2) m
[(2,1),(2,2),(2,3)]

So now we can write a function that gives you all possible paths from a given index to the bottom row of the grid.

allPathsFrom a g | fst a == fst (size g) = [[a]]
                 | otherwise             = Prelude.map (a:) xs
  where xs = concatMap (\x -> allPathsFrom x g) ys
        ys = allowedMoves a g

For example:

λ> allPathsFrom (0,1) m
[[(0,1),(1,0),(2,0),(3,0),(4,0)],[(0,1),(1,0),(2,0),(3,0),(4,1)],[(0,1),(1,0),(2,0),(3,1),(4,0)],[(0,1),(1,0),(2,0),(3,1),(4,1)],[(0,1),(1,0),(2,0),(3,1),(4,2)],[(0,1),(1,0),(2,1),(3,0),(4,0)],[(0,1),(1,0),(2,1),(3,0),(4,1)],[(0,1),(1,0),(2,1),(3,1),(4,0)],[(0,1),(1,0),(2,1),(3,1),(4,1)],[(0,1),(1,0),(2,1),(3,1),(4,2)],[(0,1),(1,0),(2,1),(3,2),(4,1)],[(0,1),(1,0),(2,1),(3,2),(4,2)],[(0,1),(1,0),(2,1),(3,2),(4,3)],[(0,1),(1,1),(2,0),(3,0),(4,0)],[(0,1),(1,1),(2,0),(3,0),(4,1)],[(0,1),(1,1),(2,0),(3,1),(4,0)],[(0,1),(1,1),(2,0),(3,1),(4,1)],[(0,1),(1,1),(2,0),(3,1),(4,2)],[(0,1),(1,1),(2,1),(3,0),(4,0)],[(0,1),(1,1),(2,1),(3,0),(4,1)],[(0,1),(1,1),(2,1),(3,1),(4,0)],[(0,1),(1,1),(2,1),(3,1),(4,1)],[(0,1),(1,1),(2,1),(3,1),(4,2)],[(0,1),(1,1),(2,1),(3,2),(4,1)],[(0,1),(1,1),(2,1),(3,2),(4,2)],[(0,1),(1,1),(2,1),(3,2),(4,3)],[(0,1),(1,1),(2,2),(3,1),(4,0)],[(0,1),(1,1),(2,2),(3,1),(4,1)],[(0,1),(1,1),(2,2),(3,1),(4,2)],[(0,1),(1,1),(2,2),(3,2),(4,1)],[(0,1),(1,1),(2,2),(3,2),(4,2)],[(0,1),(1,1),(2,2),(3,2),(4,3)],[(0,1),(1,1),(2,2),(3,3),(4,2)],[(0,1),(1,1),(2,2),(3,3),(4,3)],[(0,1),(1,2),(2,1),(3,0),(4,0)],[(0,1),(1,2),(2,1),(3,0),(4,1)],[(0,1),(1,2),(2,1),(3,1),(4,0)],[(0,1),(1,2),(2,1),(3,1),(4,1)],[(0,1),(1,2),(2,1),(3,1),(4,2)],[(0,1),(1,2),(2,1),(3,2),(4,1)],[(0,1),(1,2),(2,1),(3,2),(4,2)],[(0,1),(1,2),(2,1),(3,2),(4,3)],[(0,1),(1,2),(2,2),(3,1),(4,0)],[(0,1),(1,2),(2,2),(3,1),(4,1)],[(0,1),(1,2),(2,2),(3,1),(4,2)],[(0,1),(1,2),(2,2),(3,2),(4,1)],[(0,1),(1,2),(2,2),(3,2),(4,2)],[(0,1),(1,2),(2,2),(3,2),(4,3)],[(0,1),(1,2),(2,2),(3,3),(4,2)],[(0,1),(1,2),(2,2),(3,3),(4,3)],[(0,1),(1,2),(2,3),(3,2),(4,1)],[(0,1),(1,2),(2,3),(3,2),(4,2)],[(0,1),(1,2),(2,3),(3,2),(4,3)],[(0,1),(1,2),(2,3),(3,3),(4,2)],[(0,1),(1,2),(2,3),(3,3),(4,3)]]

Note that since GridMaps are also Grids, we can invoke all of the above functions on m or g.

λ> allPathsFrom (0,1) m

Let me know (amy at nualeargais dot ie) if you would like me to add a grid allowing diagonal moves to my grid package.

mhwombat
  • 8,026
  • 28
  • 53