I've been working on an abstract chess algorithm using Haskell (trying to expand my understanding of different paradigms), and I've hit a challenge that I've been pondering about for weeks.
Here's the problem:
Given a board (represented by a list of lists of integers; each integer represents a subsequent point value), with dimensions n x n, determine the path that provides the most points. If there is a tie for best path, return either of them.
Here are the specifics:
A = [[5,4,3,1],[10,2,1,0],[0,1,2,0],[2,3,4,20]]
which renders as:
R1: 5 4 3 1, R2: 10 2 1 0, R3: 0 1 2 0, R4: 2 3 4 20.
The rules are:
You may start anywhere on the top row
You may move one square at a time, either straight down, down-left (diagonal) , or down-right (diagonal).
The output must be a tuple of integers.
First element is a list representing the columns vs. row, and the second element is the total number of points. Eg. for the above board, the best solution is to travel from top-left (5) and go diagonally for the remaining steps (until the 20 point square). This would result in the tuple ([1,2,3,4], 29)
.
Remember, this is all in Haskell so it is a functional-paradigm recursive problem. At first, I was thinking about using the greedy algorithm, that is, choosing the highest value in r1, and recursing through comparing the next 3 possibilities; choosing the highest of the 3. However, the downfall is that the greedy algorithm doesn't have the ability to see potential ahead of the next row.
How would I go about this? I'm not looking for code per se, since I enjoy solving things on my own. However, pseudocode or some algorithmic guidance would be much appreciated!
Keep a list of the paths to each column in the row just reached with the highest score to that cell.
You'd start (in your example), with the list
Then, when checking the next row, for each column, you pick the path with the highest score in the previous row that can reach that column, here, for the second row, in column 1 and 2, you'd pick the path ending in column 1 on the row above, and in column 3, you'd pick the path ending in column 2 in the row above, in column 4, the path ending in colum 3 in the previous row, so that would give you
for the third row,
[0,1,2,0]
, you'd again pick the path ending in column 1 for the first two columns, the path ending in column 2 for the third, and the path ending in column 3 for the fourth,for the fourth row,
[2,3,4,20]
, you'd pick the path ending in column 2 for the first three columns, and the path ending in column 3 for the last,Then, when you've reached the last row, you pick the path with the highest total.
Why it works:
Let the highest-scoring path end in column
c
. The part above the last column must be the highest scoring path ending in one of the columnsc-1, c, c+1
on the penultimate row, since columnc
in the last row can only be reached from those.I chose a different path, no pun intended. I listed the allowed index combinations and mapped the board to them. Perhaps someone can find a way to generalize it to a board of any size.
I saw your previous question on the same topic, and I start to work on it.
As you doesn't want the direct solution, I can provide you my reflexion about your problem, I guess it could help you.
Some basic property :
1. The number of movement is alway egal to the length of the list m = length A
2. The number of starting point is egal to the length of the head of the list n = length (head A)
3. The current position could never be negative, then :
- if the current position is egal to 0 you can either go down or right
- else you can go to left, down or right
Which lead us to this pseudo code
This things should look like something as this
In fact keeping all of this in mind and adding the (!!) operator, we shouldn't be so far of the solution. To convince you play with A + list comprehension + !!, as
Or play with another version :
In fact you have two recursion the main one working on the parameter n = length (head A), you repeat the same action from 0 to (n-1) at (n-1) retrieve the result, this recursion embedded another one which work on m, repeat the same action from 0 to (m-1).
Hope it help. Good luck.
The best solution is not a greedy algorithm from the top down, but rather an approach that starts with the last row and works up:
It is easy to solve if there is one row. So this converts each row into a list of Result with a simple [#] solution path.
Given the
rest
for the puzzel below anew
row then adding thenew
row is a matter of finding thebest
solution fromrest
(by checking down, down left, down right) and combining with thenew
row.This makes
foldr
, or herefoldr1
the natural structure.