Random Haskell Code Challenge
Tags: haskell
Published: 2022-04-02 15:45:08

I was at work when a friend and colleague of mine showed me a funny meme, and it is about a student that tricked the teacher in a simple programming challenge, or probably a test. The requested output from the student is to create a program and produce the following result on the screen

```
ABCDEFGFEDCBA
ABCDEF_FEDCBA
ABCDE___EDCBA
ABCD_____DCBA
ABC_______CBA
AB_________BA
A___________A
```

At first, it seems that the challenge involves an algorithm "for loops" and manipulation/re-arranging of data inside the loop to achieve the desired result. The student presented a dummy non-working code that has two nested for loops and some index manipulation. These loops appear to be dead code, and the student then adds a procedure that prints the encoded output. After that, the teacher didn't bother to check the underlying logic and asked the student to run the code directly, yet the student managed to hide the encoded code in plain sight.

```c
for(int i=0; i<arr.length; i++) {
  for(int j=0; i... ) {
    // some none working code..
  }
}
printsomething()

printSomething() {

  print("ABCDEFGFEDCBA")
  print("ABCD....")
  ...
}
```

That's very funny to think of, and it's quite challenging, and I thought perhaps "Why not solve this kind of challenge in Haskell code" I'm not an expert in Haskell, nor do I use Haskell in my day-to-day work, but in any case, it's a good exercise so let's jump in.

Without a problem definition, we assume that we need to print "A" to "G" and after that, it will go back from "F" to "A", and at the very least that's how it appears in the first line. We could then apply this rule for all the succeeding lines, and just with an added twist we also eliminate the characters in the middle until there's only one character left to print out.

## First Step: Generate a Data

Generate a list of lists, and each list will have different lengths

list 1 of length 7
list 2 of length 6
list 3 of length 5
...

we then put this on a list, that would be

```haskell
[ list1, list2, list3, ... ]
```

as of now this list contains ordered numbers incremented by 1, we achieve that by Haskell infinite list and take only the desired length.

```haskell
take 5 [1..] -- take 5 elements in an infinite list.
-- [1, 2, 3, 4, 5]
```

Okay, so far we have a function to build a list of numbers incrementing by 1. Next, we generate a list of length 7 used as a row, this then will be mapped to generate a list as a length, let's call this rangeMatrix.

```haskell
import Data.List

rangeMatrix = fmap (\n -> take n [1..]) [1..7]
-- [ [1]
-- , [1, 2]
-- , [1, 2, 3]
-- , [1, ... ]
-- ...
-- ]
```

The `fmap` here is like in javascript `Array.map`, which maps each element in the list using a mapper function, in our case the mapping function accepts an integer (n) and returns a list taking only (n) elements.

Now that we have a generated matrix of numbers, we convert these numbers to our desired characters "A" to "G", we then create a simple mapping function to achieve this

```haskell
conv :: Int -> Char
conv 1 = 'A'
conv 2 = 'B'
conv 3 = 'C'
conv 4 = 'D'
conv 5 = 'E'
conv 6 = 'F'
conv 7 = 'G'
conv _ = ' '
```

then we map the rangeMatrix numeric to converted character.

```haskell
rangeMatrixChars :: [[Int]] -> [[Char]]
rangeMatrixChars xss = fmap (fmap conv) xss
-- [ ['A']
-- , ['A', 'B']
-- , ['A', 'B', 'C']
-- , ['A', ... ]
-- ...
-- ]
```

## Second step: Format Data

The next step and the most interesting part is to format this matrix to a readable string. It is a common practice in functional programming to design modular functions, each of which has responsibilities. In our case, we just currently designed a function that generates data, and now we will create a function that accepts our matrix characters and process it to a single list of characters (also equivalent to `String` in Haskell).

```haskell
prettyFormat :: [[Char]] -> [Char]
prettyFormat xss =
 foldl prettyFormatStep [] xss
```

and each step of the format will do

```haskell
prettyFormatStep :: Int -> [Char] -> [Char] -> [Char]
prettyFormatStep maxChar [] xs =
  xs
prettyFormatStep maxChar acc xs =
  xs
  ++ ['\n']
  ++ acc
```
```
ABCDEFG
ABCDEF
ABCDE
ABCD
ABC
AB
A
```

We're getting closer to the desired output. In addition, we need to tweak it out a little bit. Next, we add negative space, to show disappearing characters. As of now, we use the `*` character

```haskell
negativeSpaceFormat maxChar whiteSpaceChar xs = 
  replicate (abs (length xs - maxChar)) whiteSpaceChar
```

and in `prettyFormatStep` will be

```diff
prettyFormatStep maxChar [] xs =
  xs
+ ++ (negativeSpaceFormat maxChar '*' xs ++ reverse xs)

prettyFormatStep maxChar acc xs =
  xs
+ ++ (negativeSpaceFormat maxChar '*' xs ++ reverse xs)
  ++ ['\n']
  ++ acc
```

```
ABCDEFG
ABCDEF*
ABCDE**
ABCD***
ABC****
AB*****
A******
```

and finally, we add the mirror of the output per row.

```diff
prettyFormatStep maxChar [] xs =
  xs
  ++ negativeSpaceFormat maxChar '*' xs
+ ++ (negativeSpaceFormat maxChar '*' xs ++ reverse xs)

prettyFormatStep maxChar acc xs =
  xs
  ++ negativeSpaceFormat maxChar '*' xs
+ ++ (negativeSpaceFormat maxChar '*' xs ++ reverse xs)
  ++ ['\n']
  ++ acc
```

Since the negative whitespace characters are in symmetry we don't need to reverse it, but in general, the reversed will also be applied. The final output would be

```
ABCDEFGGFEDCBA
ABCDEF**FEDCBA
ABCDE****EDCBA
ABCD******DCBA
ABC********CBA
AB**********BA
A************A
```

But wait! We have an error, the output so far is not the same as the expected output at the beginning of this article. If we notice the first row has a duplicate `G` and the second row has an extra `*` for each row. The problem is that we mirror exactly our data which is the exact inverse of the list. We could fix this by removing the first element of the inversed list. We can use the function `tail` to fix this.

```diff
prettyFormatStep maxChar [] xs =
  xs
  ++ negativeSpaceFormat maxChar '*' xs
- ++ (negativeSpaceFormat maxChar '*' xs ++ reverse xs)
+ ++ tail (negativeSpaceFormat maxChar '*' xs ++ reverse xs)

prettyFormatStep maxChar acc xs =
  xs
  ++ negativeSpaceFormat maxChar '*' xs
- ++ (negativeSpaceFormat maxChar '*' xs ++ reverse xs)
+ ++ tail (negativeSpaceFormat maxChar '*' xs ++ reverse xs)
  ++ ['\n']
  ++ acc
```

```
ABCDEFGFEDCBA
ABCDEF*FEDCBA
ABCDE***EDCBA
ABCD*****DCBA
ABC*******CBA
AB*********BA
A***********A
```

If you read the article up to this point you deserve applause 👏 we did arrive at the expected output.

There are a few optimization and syntax that needs to be adjusted, some of you may have a better understanding of Haskell may point out *eta reduction*, *list comprehension*, and many more. I leave it to you to tinker with this solution.

Thank you, and have fun coding

Full runnable source code: https://replit.com/@RonnelReposo/SimplisticAffectionatePackagedsoftware#Main.hs