1080 1033 bytes
hi = g
g ' ' =0
g '"' =0;
g '$' =0;
g '&' =0-0
g '(' =0-0-0
g '*' =0-0-0;
g ',' =0-0-0;
g '.' =0-0-0-0
g '0' =0-0-0-0-0
g '2' =0-0-0-0-0;
g '4' =0-0-0-0-0;
g '6' =0; g '8' =0
g ':' =0; g '<' =0-0
g '>' =0; g '@' =0-0;
g 'B' =0; g 'D' =0-0;
g 'F' =0; g 'H' =0-0-0
g 'J' =0; g 'L' =0-0-0-0
g 'N' =0; g 'P' =0-0-0-0;
g 'R' =0; g 'T' =0-0-0-0;
g 'V' =0; g 'X' =0-0-0-0-0
g 'Z' =0; g '^' =0; g '`' =0
g 'b' =0; g 'd' =0; g 'f' =0;
g 'h' =0; g 'j' =0; g 'l' =0;
g 'n' =0; g 'p' =0; g 'r' =0-0
g 't' =0; g 'v' =0; g 'x' =0-0-0
g 'z' =0; g '\92' =0-0; g '|' =0;
g '~' =0; g y = 1 ;z=0; i(-0)z=z;
i m('\10':y ) ="y"; ; ; ; ; ; ; ;
i m(mnmnmnmnm:y ) = i(m - 1 ) y ; ;
i k m ="y"; ; k i [ ] =01<1010101010;
k m('\10':y ) = k(m + 1 )(i m y ) ; ;
k m y =01>10; m o = k 1$'\10':o ; ; ;
o i('\10':y ) = o i y ; ; ; ; ; ; ; ; ;
o i(k:y )|g k<i = o(1 - i ) y ; ; ; ; ; ;
o i(k:y )|g k>i = o(1 - i ) y ; ; ; ; ; ;
o i [ ] =01<10; o i y =01>10;v=01>10101010
s y|o 1 y = m y|o(-0) y = m y ; s y =v; ; ;
Try it online!
This has been quite the interesting task for Haskell.
To start we need some way of determining if a character has an even or odd code-point. The normal way one might do this is to get the code-point and mod it by 2. However as one might be aware, getting the code-point of a character requires an import, which due to the source restriction means that it cannot be used. A more experienced Haskeller would think to use recursion.
Char's are part of the
Enum typeclass so we can get their predecessors and successors. However
succ are also both unusable because they don't alternate byte parity.
So this leaves us pretty stuck we pretty much can't do any manipulation with chars. The solution to this is to hardcode everything. We can represent (most) even chars as literals, odds we have trouble with because
' is odd so it can't be next to the char itself making the literal impossible to express most of the odd chars. So we hard code all of the even bytes, and then add a catch all for the odd bytes at the end.
The problem Bytes
You may notice that there are some even bytes for which literals cannot be made by wrapping it in single-quotes. They are the unprintables, newlines and
\. We don't need to worry about unprintables since as long as we don't use any of them we don't need to verify. In fact we can still use odd unprintables, like tab, I just don't end up needing to. Newline can sagely be ignored because it will be trimmed from the program anyway. (We could include newline, because it's code-point is rather convenient, but we don't need to). This leaves
\ has the codepoint 92, which conveniently is an odd number followed by an even number, so
\92 alternates between evens and odds thus the literal
'\92' is perfectly valid. Later on when we do need to represent newline we will notice that it luckily has this same property
Now in order to start writing actual code we need to be able to put a sizable number of characters on a single line. In order to do this I wrote the cap:
hi = g
The cap doesn't do anything except be valid Haskell.
I had initially hoped to make definitions that would help us in the code later, but it didn't. There are also easier ways to make the cap, for example whitespace and semicolons, but they don't save bytes over this way so I haven't bothered to change it.
So now that I have enough space on a line I start hardcoding values. This is mostly pretty boring, but there are a few things of interest. For one once the lines start getting even longer we can use
; to put multiple declarations on a line, which saves us a ton of bytes.
The second is that since we can't always start a line with a
g every so often we have to indent the lines a bit. Now Haskell really cares about indentation, so it will complain about this. However if the last line before the indented line ends in a semicolon it will allow it. Why? I haven't the faintest, but it works. So we just have to remember to put the semicolons on the end of the lines.
Function Building Blocks
Once the hardcoder is done it is smooth sailing to the end of the program. We need to build a few simple functions. First I build a version of
i is different from
drop in that if we attempt to drop past the end of the string it just returns
i is different from drop also in that if it attempts to drop a newline it will return
"y", These will be useful because later when we are verifying that the program is a triangle this will allow us to return
False when the last line is not complete, or when a line ends early.
Next we have
k which in fact verifies that some string is triangular.
k is pretty simple, it takes a number \$n\$ and a string \$s\$. If \$s\$ is empty it returns
True. If the string begins with a newline it removes the newline and \$n\$ characters from the front. It then calls
k again with \$n+1\$ and the new string. If the string does not start with a newline it returns
We then make an alias for
m is just
1 in the first argument, and a newline prepended to the second argument.
Next we have
o takes a number and a string. It determines if the string bytes (ignoring newlines) alternate in parity (using our
g) starting with the input number.
Lastly we have
s which runs
o with both
0, if either succeeds it defers to
m. If it fails both it just returns
False. This is the function we want. It determines that the input is triangular and alternating.