Klondike Solitaire for curses in 5k

by Oscar Toledo G. Jun/07/2026
Obfuscated C and Other Mysteries book by Don Libes.
I had some free time, and I noticed the 29th IOCCC started. So I decided to code an entry for the contest. If you haven't heard about the International Obfuscated C Code Contest, it is a contest running since 1984 and created by Landon Curt Noll. The objective of this contest is writing an obfuscated C program under certain size limitations specified by the rules.
You can see my previous winning entries for the IOCCC in the Contests section.
This year the maximum size is 4993 bytes (a little less than 5 kilobytes), and the number of printable characters is 2503. As there are some special rules for printable characters, there is now a tool called iocccsize for checking this.
Whenever I say obfuscated C, it is the way of writing C code in a form that does the objective but it isn't clear. For example, a simple loop counting from 1 to 10 is written like this:

#include <stdio.h>
int main(void) {
    int c;
    
    for (c = 1; c <= 10; c++)
      printf("%d\n", c);
}
But using a small fraction of the C syntax power, we can do it this way:

#include <stdio.h>
int main(void) {int c=1;while(printf("%d\n",c),11>++c);}
This gives you a small idea of what obfuscated C is about. Deciphering the best one liner of any IOCCC year is a challenge! And a way of testing your C knowledge.

The idea

After pondering several ideas, I decided for a Klondike Solitaire game in C language as I have coded several over my career. One for my own operating system about twenty years ago (I need to write more about this), and recently Klondike for Intellivision consoles.
In case you don't know, Solitaire was a game included in Windows 3.1 along Minesweeper. Both were a complete success because the gaming scene for Windows was almost null. Bored people using Windows 3.1 started playing Minesweeper and then Solitaire, and it was addictive! I didn't know until I played myself, and I understood the rules. I also discovered that there are several solitaire games, also known as patience, and this was the Klondike variation. I leaved it for good when I discovered that not all games could be won. In fact, mathematicians estimate that only 43% of the games with a single card deal can be won, and this number drops sharply to 18% if three cards are deal. Even if you can see all the cards it isn't so easy to win.
Klondike Solitaire in Windows 3.1
Klondike Solitaire in Windows 3.1 (running in PCjs Machines)

Coding it!

I started coding in Feb/06/2026, it took me three days to code the main logic in C, and I decided to use the curses library to create the cards display, use color, and added also Unicode characters to show the cards symbols. The curses library allows to control the screen for creating text interfaces without worrying about the terminal type being used.
Of course, this first version of the game was too big for the contest's limits, so I started optimizing the code. Also I had to solve the problem of the user interface for selecting and dropping the cards.
The original interface of Klondike is oriented towards moving a cursor over a card, picking it, and dropping it in another place. In fact, the legend says that Windows 3.1 included this game just to teach users how to drag&drop things with the mouse.
In order to fit in the space for the contest, my user interface has to be greatly simplified using the Tab key for selecting cards, and the Space key for dropping cards.

I don't know how to use curses

Learning curses is pretty good if you are an evil wizard... Oh sorry, I mean learning the curses library :P It felt like an eternity the learning curve for using it in the game. I discovered there are several versions of it, and I know by experience that every Linux and Mac distribution includes it in some form.
The main problem of curses is that there are way too many versions! And each operating system integrates a vaguely different version of the library. The other common problem is that the suits require UTF-8, and some curses libraries don't support it (see the Remarks). You are good with curses if you are using ASCII exclusively.
At least I found character definitions for building boxes (the cards), and Unicode provided me the symbols for the cards.
I also could use colors for red cards. Many years ago it was far more common using the ANSI/VT-52 escape sequences, but this is the type of thing that curses solves through an unified interface.
My Klondike game for curses in action
My Klondike game for curses in action

Polishing

I managed to implement a 3-card deal as an option, and also a Las Vegas scoring mode. The number of arguments when running the program selects these options (see the Remarks)
The cherry in the cake was adding a scoring system exactly like in Windows, including the bonus per time.

The source code

After several revisions and days in development, here is the source code of my Klondike Solitaire game for curses.

#include <stdlib.h>
#include <locale.h>
#include <time.h>
#include <curses.h>
#define H addch
#define L(z) for(z=0;z<52;z++)
#define _ H(ACS_CKBOARD);
#define O 255
#define I if(
#define E else
#define J H(ACS_HLINE);
int W(int *,int *);

             int                 k[O ],         x[ O ],
            b[O],             v [ O ],i,s,     m,a ,z,e,
          w,n,d,t,c,          h,g,j,f,l,y;   void  F(void)
         {d=c[k]; e=          c[x]; f=c[b]; for(g=c;g<51;g
        [k]=k[1+g],g[x]      =x[g+1],g[b]=b[g+1],g++);g[k]=
       d; g[x]=e; g[b]=f;     c=g; } void U(void) { d = O;
      L(c) I !c[x]) d = c;     c = d; I d - O) { c[x] =1;
     b[c] = 0; F(); d = c;      } } void A(void) { m = 1;
     t = O;  l =0;  d = O;       L(c) I !c[x] & b[c]) d
      = c;    I d    - O)         v[l++] = d; e = O;
      L(c)    I x    [c]             == 1 && !c[b])
         e    = c    ;I               e - O ) { I
            d == O                     ) v[l++] =
           d; v[l++                      ] = e;
                                           }

              L(c)                       I c[x]
             >  31                      && !c[b])
            v[l++ ]                    = c; I l>0)
           I v[0] ==                    O) j = 0;
          E j=x[v[0]];                   qsort(
         v,l,sizeof(m),                   &W);}
        void K (void )            { t      = c;     d =
       0; I c[x] >  31)          { L(e)     I      (e[x]
      & 7) == (c[x] & 7))       { I x[e]   > x    [c]) d =
     1; } } } void M(void)      { I c[x] ==e) return; I n &
      2) { I 1 == c[x]) {         I a > 1) a --   ; }  } I
       n & 1) { I e >  2           & e      <      7) s +=
        5; } E { I e >              2      & e       <
         7) s += 10; E                     I x
          [c] == 1) s                      +=5;
           } g = O ;                      L(f) {
             I x[f]                       == c[x]
              - 8)
               {
                   
I b[f]) { I ~n & 1) s += 5; b[f] = 0; g = f; } } } do { h = c[x] + 8;
c[x] = e; F(); e = c[x] + 8; c = O; L(f) I x[f] == h) c = f; } while
(c - O) ; t = O; } int W(int *e, int *h) { return x[*e]-x[*h]; }
int main(int r) { char *S;time_t D=time(0);setlocale(LC_CTYPE, S="");
srand(D); L(c) { for (; v[d = rand() % 52]; ) ; d[b]=d[v]=1;
d[k]=c; } c = 24; for (d = 0; d < 7; d++) for (e = 0; e < d + 1; x[c++] =
d + (e++ + 4) * 8) ; b[24] = b[26] = b[29] = b[33] = b[38] = b[44] = b[51]
= 0; initscr(); raw(); noecho(); start_color(); init_pair(1, COLOR_RED,
COLOR_WHITE); init_pair(2, COLOR_BLACK, COLOR_WHITE); A(); n = r - 1;
I n & 1) s = -52; for(;;) { for (c = 1; c < 5; c++) { for (e = 0; e < 7;
e++) { I e == 2) continue; move(c, e * 6); _ _ _ _ _
I e == 1) { H(32); H(32); H(32); H(32); } } }
L(e) { c = x[e]; g = (c & 7) * 6; I c == 1) { I a > 2 & k[e] == z)
g += 4; I a > 1 & k[e] == y) g += 2; }
h = k[e] / 13; i = k[e] % 13; for(d=0;d<4;d++){ move(c / 8 * 2 + d + 1, g);
if (!d) { H(ACS_ULCORNER); J J J H(ACS_URCORNER); } E if (d == 3) {
H(ACS_LLCORNER); J J J H(ACS_LRCORNER); } E { H(ACS_VLINE);
if (b[e]) { _ _ _ } E { I h < 2) attron(COLOR_PAIR(1));
E attron(COLOR_PAIR(2)); if (d == 2) { H(32); H(32); H(32); } E { I !i)
H(65); E I i < 9) H(49 + i); E I i == 9) { H(49); H(48); } E {
H("JQK"[i - 10]); }
addstr(&"\xe2\x99\xa5\0\xe2\x99\xa6\0\xe2\x99\xa0\0\xe2\x99\xa3"[h * 4]);
I i - 9) H(32); } I h < 2) attroff(COLOR_PAIR(1)); E attroff(COLOR_PAIR(2));
} H(ACS_VLINE); } } } attron(A_NORMAL); w = j % 8 * 6; I j == 1) I n & 2)
w += (a - 1) * 2; move(j / 8 * 2 + 1, w); H(42); I t - O) {
c = x[t]; w = c % 8 * 6; I c == 1) { I n & 2) w += (a - 1) * 2; }
move(c / 8 * 2 + 1, w); H(38); } mvprintw(0, 0, "%sScore: %d   ", S, s);
refresh(); c = getch(); I c == 10) break;
I c == 32) { I m) { I !j) { I v[0] == O) { do { d = O; L(c) I c[x] == 1)
d = c; c = d; I c - O) { c[x] = 0; b[c] = 1; F(); }
} while (c != O); } E { I n & 2) { a = 0; U(); I d - O) a++; U();
I d != O) a++, y = k[d]; U(); I d != O) a++; z = k[d]; } E U();
d = O; L(c) I !c[x]) d = c; } A(); } E { L(d) I j == x[d]) c = d;
K(); m = 0; l = 0; I !d) { for (e = 3; e < 7; e++) { g = O;
L(f) I x[f] == e) g = f; I (g == O & k[c] % 13 == 0) | (g != O &
k[c] % 13 == k[g] % 13 + 1 & k[c] / 13 == k[g] / 13)) { v[l++] = e;
} } } for (h = 0; h < 7; h++) { f = h + 32; g = O; L(e)
I (x[e] & 7) == h & x[e] > 31 & x[e] + 8 > f) { f = x[e] + 8; g = e; }
I (g == O & k[c] % 13 == 12) | (g != O & k[g] % 13 - 1 == k[c] % 13 &
((k[g] / 13) & 2) != ((k[c] / 13) & 2))) v[l++] = f; } I l) j = v[0]; } }
E { c = t; e = j; M(); d = 0; L(c) d+=c[x] < 3 | c[x] > 6; I !d)
S="Won!",s+=n&1?0:700000/difftime(time(0),D);A(); clear();
} } E I c == 9 && l) { I m) { for (c = 0; c < l; c++) I j == ((d = v[c])
- O ? x[d] : 0)) break; j = (d = v[++c % l]) - O ? x[d] : 0; } E { I l)
{ for (c = 0; c<l&j!=v[c]; c++) ; j = v[++c % l]; } } } } endwin(); }

Remarks

Klondike Solitaire

Building it

gcc prog.c -o prog

Executing it


    prog
    prog a
    prog a b
    prog a b c
    

About it

Now you can play Klondike solitaire with this program.
It requires ncurses to be built, and UTF-8 support. Any recent operating system will do it (tested with Terminal and macOS 12.7.6)
In Fedora 21 (2014 is so old!) the card graphics doesn't appear, and you need to change the -lcurses linker option to -lcursesw
I tested also macOS 10.15 (Catalina), and I had Homebrew installed, so I typed brew install ncurses and it went with an automatic update to get 533 megabytes from Github. The symbols appear, but no font contains the card borders and background. I find interesting that an operating system from 2020 doesn't have the symbol support that Linux has since 2014.
I also checked with macOS 10.11 (El Capitan), but the default ncurses v5 doesn't support UTF-8. I tried installing Homebrew but it couldn't talk to Github.
You are suggested to resize the window for a 36 or 40 line terminal.
It will generate warnings on compilation to confuse you.

Playing it

You can execute it without arguments to get a Klondike solitaire game with Windoze scoring system, including bonus time scoring.
Add one argument to get a Las Vegas scoring.
Add two arguments for Windoze scoring system but dealing 3 cards.
Add three arguments for Las Vegas scoring system but dealing 3 cards.

Game keys

You can displace the cursor (an asterisk) using the Tab key, and select the card to move or drop using the Space bar.
Press Space bar in the top left to deal cards.
If you feel bored, press Enter to exit the game.

Obfuscation tricks

Everyone knows that the != operator is a surplus of the C language, use only the minus operator.
Same for the >= operator, use only >.
Save a curly brace today, use for.
Also when writing array accesses, feel the vibe, and interchange randomly the array and the index.
Use O for constants so it looks like zero.
Let's throw some trinary operators for good measure.
The best obsfucation was achieved when I added the code for the clock to give the bonus time scoring. Just like in real software.
My Klondike game for curses being played
My Klondike game for curses being played

Download

You can download my entry for the IOCCC here:

Post Mortem

Later, after the contest closed, I noticed there were some more obfuscation chances in the code, and possible optimization paths.
I enjoyed very much the live announcement of the winners, and unfortunately my entry didn't won.
I'm glad to share my entry so you can have fun playing Klondike in your text console, and of course I can claim this is the smallest solitaire game in C language ;)
Did you enjoyed this article? Share it, invite me a coffee on ko-fi, or become a monthly supporter. Your support allows me to dedicate more time to write articles like this one.

Links

Last modified: Jun/08/2026