Generate ladder of integers using the least number of unique characters (in C++)

I believe I managed to remove the = character from your code, although it now is significantly slower

#include<iostream>

int i;
int ii;
int iii;
int iiii;

int main() {
    std::cin >> i;
    i++;
    for(ii++; ii < i;) {
    for(;iii>iiii;iii++);
    for(;iii<iiii;iii++);
    ii++;
        for(iii++; iii < ii; iii++) {
            std::cout << iii << " ";
        }
        std::cout << std::endl;
    }
}

Its not pretty, but by abusing integer overflow we can get back to 0 without using =

Also we had to change the guards a little. Unfortunately because of the include I couldn't get rid of all new line characters (although its close) so that may be the next avenue for investigation.

Edit: Run out of time for now, but if you include and use strstream and various other libraries, I think you may be able to remove the " character too, again using integers to arrive at the correct character for space and passing it into the strstream


I finally got 24 unique characters by combining the answers of @ExpiredData and @someone. Also, using the short data type instead of int helped to speed up my program because it takes a shorter time to overflow a short data type.

My code is as follows.

%:include<iostream>

short i;
short ii;
short iii;
short iiii;
char iiiii;

main() <%
    std::cin >> i;
    iiiii++;iiiii++;iiiii++;iiiii++;iiiii++;iiiii++;iiiii++;iiiii++;
    iiiii++;iiiii++;iiiii++;iiiii++;iiiii++;iiiii++;iiiii++;iiiii++;
    iiiii++;iiiii++;iiiii++;iiiii++;iiiii++;iiiii++;iiiii++;iiiii++;
    iiiii++;iiiii++;iiiii++;iiiii++;iiiii++;iiiii++;iiiii++;iiiii++;
    i++;
    for(ii++; ii < i; ii++) <%
        for(;iii;iii++);
        for(iii++; iii < ii; iii++)
            std::cout << iii << iiiii;
        std::cout << iii << std::endl;
    %>
%>

23 unique characters using Digraphs. (25 without). No UB.

Use C++11 braced initializer syntax to list-initialize an integer to zero with int var{}; avoiding = and 0. (Or in your case, avoiding global iiii). This gives you a source of zeros other than global variables (which are statically initialized to zero, unlike locals).

Current compilers accept this syntax by default, without having to enable any special options.

(The integer wraparound trick is fun, and ok for golfing with optimization disabled, but signed overflow is undefined behaviour in ISO C++. Enabling optimization will turn those wraparound loops into infinite loops, unless you compile with gcc/clang -fwrapv to give signed integer overflow well-defined behaviour: 2's complement wraparound.

Fun fact: ISO C++ std::atomic<int> has well-defined 2's complement wrap-around! int32_t is required to be 2's complement if defined at all, but the overflow behaviour is undefined so it can still be a typedef for int or long on any machine where one of those types is 32 bits, no padding, and 2's complement.)


Not useful for this specific case:

You can also initialize a new variable as a copy of an existing one, with either braces or (with a non-empty initializer), parens for direct initialization.
int a(b) or int a{b} are equivalent to int a = b;

But int b(); declares a function instead of a variable initialized to zero.

Also, you can get a zero with int() or char(), i.e. zero-initialization of an anonymous object.


We can replace your <= compares with < compares by a simple logic transformation: do the loop-counter increment right after the compare, instead of at the bottom of the loop. IMO this is simpler than the alternatives people have proposed, like using ++ in the first part of a for() to make a 0 into a 1.

    // comments aren't intended as part of the final golfed version
    int n;
    std::cin >> n;      // end condition

    for(int r{}; r < n;) {      // r = rows from 0 .. n-1
        ++r;
        for(int i{}; i < r;) {
            ++i;
            std::cout << i << ' ';
        }
        std::cout << std::endl;
    }

We could golf that down to for(int r{}; r++ < n;) but IMO that's less easy for humans to read. We're not optimizing for total byte count.


If we were already using h, we could save the ' or " for a space.

Assuming an ASCII or UTF-8 environment, space is a char with value 32. We can create that in a variable easily enough, then cout << c;

    char c{};
    c++; c++;            // c=2
    char cc(c+c+c+c);    // cc=8
    char s(cc+cc+cc+cc); // s=32 = ' ' = space in ASCII/UTF-8

And other values can obviously be created from a sequence of ++ and doubling, based on the bits of their binary representation. Effectively shifting a 0 (nothing) or 1 (++) into the LSB before doubling into a new variable.


This version uses h instead of ' or ".

It's much faster than either of the existing versions (not relying on a long loop), and is free of Undefined Behaviour. It compiles with no warnings with g++ -O3 -Wall -Wextra -Wpedantic and with clang++. -std=c++11 is optional. It is legal and portable ISO C++11 :)

It also doesn't rely on global variables. And I made it more human-readable with variable names that have a meaning.

Unique-byte count: 25, excluding the comments which I stripped with g++ -E. And excluding space and newline like your counter. I used sed 's/\(.\)/\1\n/g' ladder-nocomments.cpp | sort | uniq -ic from this askubuntu to count occurrences of each character, and piped that into wc to count how many unique characters I had.

#include<iostream>

int main() {
    char c{};
    c++; c++;            // c=2
    char cc(c+c+c+c);    // cc=8
    char s(cc+cc+cc+cc); // s=32 = ' ' = space in ASCII/UTF-8

    int n;
    std::cin >> n;      // end condition

    for(int r{}; r < n;) {      // r = rows counting from 0
        ++r;
        for(int i{}; i < r;) {
            ++i;
            std::cout << i << s;
        }
        std::cout << std::endl;
    }
}

The only 2 f characters are from for. We could use while loops instead if we had a use for w.

We could possibly rewrite the loops into an assembly-language style of i < r || goto some_label; to write a conditional jump at the bottom of the loop, or whatever. (But using or instead of ||). Nope, that doesn't work. goto is a statement like if and can't be a sub-component of an expression like it can in Perl. Otherwise we could have used it to remove the ( and ) characters.

We could trade f for g with if(stuff) goto label; instead of for, and both loops always run at least 1 iteration so we'd only need one loop-branch at the bottom, like a normal asm do{}while loop structure. Assuming the user inputs an integer > 0...


Digraphs and Trigraphs

Fortunately, trigraphs have been removed as of ISO C++17 so we don't have to use ??> instead of } if we're unique-golfing for the most recent C++ revision.

But only trigraphs specifically: ISO C++17 still has digraphs like :> for ] and %> for }. So at the cost of using %, we can avoid both { and }, and use %: for # for a net saving of 2 fewer unique characters.

And C++ has operator keywords like not for the ! operator, or bitor for the | operator. With xor_eq for ^=, you could zero a variable with i xor_eq i, but it has multiple characters you weren't using.

Current g++ already ignores trigraphs by default even without -std=gnu++17; you have to use -trigraphs to enable them, or -std=c++11 or something for strict conformance to an ISO standard that does include them.

23 unique bytes:

%:include<iostream>

int main() <%
    int n;
    std::cin >> n;

    for(int r<% %>; r < n;) <%
        ++r;
        for(int i<%%>; i < r;) <%
            ++i;
            std::cout << i << ' ';
        %>
        std::cout << std::endl;
    %>
%>

Try it online!

The final version uses a ' single-quote instead of h or " for the space separator. I didn't want to digraph the char c{} stuff so I deleted it. Printing a char is more efficient than printing a string, so I used that.

Histogram:

$ sed 's/\(.\)/\1\n/g' ladder-nocomments.cpp | sort | uniq -ic  | tee /dev/tty | wc -l
     15         // newline
     95         // space
     11 %
      2 '
      3 (
      3 )
      4 +
      9 :
     10 ;
     14 <
      8 >
      2 a
      4 c
      6 d
      3 e
      2 f
     12 i
      2 l
      2 m
     11 n
      5 o
      7 r
      5 s
     11 t
      3 u
25   // total lines, including space and newline

The space separator (still unsolved)

In a now-deleted answer, Johan Du Toit proposed using an alternate separator, specifically std::ends. That's a NUL character, char(0), and prints as zero-width on most terminals. So the output would look like 1234, not 1 2 3 4. Or worse, separated by garbage on anything that didn't silently collapse '\0'.

If you can use an arbitrary separator, when the digit 0 is easy to create with cout << some_zeroed_var. But nobody wants 10203040, that's even worse than no separator.

I was trying to think of a way to create a std::string holding a " " without using char or a string literal. Maybe appending something to it? Maybe with a digraph for [] to set the first byte to a value of 32, after creating one with length 1 via one of the constructors?

Johan also suggested the std::ios fill() member function which returns the current fill character. The default for a stream is set by std::basic_ios::init(), and is ' '.

std::cout << i << std::cout.fill(); replaces << ' '; but uses . instead of '.

With -, we can take a pointer to cout and use ->fill() to call the member function:
std::cout << (bitand std::cout)->fill(). Or not, we weren't using b either so we might as well have used & instead of its lexical equivalent, bitand.

Calling a member function without . or ->

Put it inside a class, and define operator char() { fill(); }

// not digraphed
struct ss : std::ostream {  // default = private inheritance
//      ss() { init(); }  // ostream's constructor calls this for us
        operator char() { return fill(); }
}

Then ss s{} before the loop, and std::cout << i << s; inside the loop. Great, it compiles and works properly, but we had to use p and h for operator char(), for a net loss of 1. At least we avoided b to make member functions public by using struct instead of class. (And we could override the inheritance with protected in case that ever helps).