Skip to content
HN On Hacker News ↗

Klondike Solitaire for curses in 5k of C language

▲ 102 points 17 comments by nanochess 2w ago HN discussion ↗

Pangram verdict · v3.3

We believe that this document is fully human-written

0 %

AI likelihood · overall

Human
100% human-written 0% AI-generated
SEGMENTS · HUMAN 8 of 8
SEGMENTS · AI 0 of 8
WORD COUNT 2,183
PEAK AI % 1% · §6
Analyzed
Jun 11
backend: pangram/v3.3
Segments scanned
8 windows
avg 273 words each
Distribution
100 / 0%
human / AI fraction
Verdict
Human
Pangram v3.3

Article text · 2,183 words · 8 segments analyzed

Human AI-generated
§1 Human · 0%

by Oscar Toledo G. Jun/07/2026

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.

§2 Human · 0%

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 (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.

§3 Human · 0%

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 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 !

§4 Human · 0%

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]

§5 Human · 1%

+ 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 (!

§6 Human · 1%

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 !

§7 Human · 0%

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 !

§8 Human · 0%

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.