Play peg solitaire on the Linux command line

peg-solitaire

Peg solitaire rules

Peg solitaire is a very old game that you can play alone to kill some time. Classic versions are made of a wooden playfield with indentations for either wooden pegs or glass marbles on them. The objective is to remove one peg from the center and jump pegs until you have only one peg left in the center.

When one peg jumps over another peg, then the peg that you jumped over is removed from the playing field. The peg that made the jump is not removed and is now located either 2 positions up, left, right or down from it’s initial position. Diagonal jumps are not allowed, nor are jumps over multiple pegs at once.

Peg solitaire layouts

peg-solitaire_layouts

There are several layouts of the playing field. The picture above shows the layouts that are most common. The French layout is also called the “European” layout. Special about the European layout is that you cannot start and finish in the center, but you should start one peg above the center and end one peg below the center.

Implementation in C

I have written a Peg solitaire implementation in C for the Linux command line, just like I did with the 2048 game. It is again a single C file that you can easily compile with gcc and has no external dependencies. You can start the application with the layout name (from the picture above) as the first argument. If you did not provide any arguments the most common layout (English) is chosen.

wget https://raw.githubusercontent.com/mevdschee/peg-solitaire.c/master/peg-solitaire.c
gcc -o peg-solitaire peg-solitaire.c
./peg-solitaire french

Linux ANSI codes for command-line games

When you are going to write games for the command line you will probably need ANSI control sequences. ANSI control sequences allow you to: clear the screen, show or hide the cursor, move the cursor position, change the color of the characters and their background. Here is an overview of the most useful ANSI control sequences when creating console games:

printf("\e[2J");      // clear screen and move cursor to root
printf("\e[#;#H");    // move cursor to position #,# (default 1,1)
printf("\e[#A");      // move cursor # lines up (default 1)
printf("\e[#B");      // move cursor # lines down (default 1)
printf("\e[#C");      // move cursor # columns right (default 1)
printf("\e[#D");      // move cursor # columns left (default 1)
printf("\e[?25l");    // hide cursor
printf("\e[?25h");    // show cursor
printf("\e[0m");      // reset attributes (and color)
printf("\e[1m");      // set attribute bold
printf("\e[2m");      // set attribute half-bright
printf("\e[4m");      // set attribute underscore
printf("\e[7m");      // set attribute reverse video
printf("\e[21m");     // unset attribute bold
printf("\e[22m");     // unset attribute half-bright
printf("\e[24m");     // unset attribute underscore
printf("\e[27m");     // unset attribute reverse video
printf("\e[3#m");     // set foreground color to # (0-7)
printf("\e[4#m");     // set background color to # (0-7)
printf("\e[38;5;#m"); // set foreground color to # (0-255)
printf("\e[48;5;#m"); // set background color to # (0-255)

Check the ANSI palette script to see what colors are possible:

bash_256_colors

To get a good overview of supported codes in Linux read the man page:

man console_codes

Disable input buffering and local echo in Linux

Normally input is buffered and only when you press the “enter” key the input is readable from the standard input of the application. Also input is shown on the screen. When you are programming a game you don’t want this. You need to respond directly when a key is pressed and there is no need to display the pressed keys. This can be achieved by disabling input buffering and local echo. In C this is done with the following function:

#include <termios.h>
#include <stdbool.h>

void setBufferedInput(bool enable) {
	static bool enabled = true;
	static struct termios old;
	struct termios new;

	if (enable && !enabled) {
		// restore the former settings
		tcsetattr(STDIN_FILENO,TCSANOW,&old);
		// set the new state
		enabled = true;
	} else if (!enable && enabled) {
		// get the terminal settings for standard input
		tcgetattr(STDIN_FILENO,&new);
		// we want to keep the old setting to restore them at the end
		old = new;
		// disable canonical mode (buffered i/o) and local echo
		new.c_lflag &=(~ICANON & ~ECHO);
		// set the new settings immediately
		tcsetattr(STDIN_FILENO,TCSANOW,&new);
		// set the new state
		enabled = false;
	}
}

For the full source code check out: https://github.com/mevdschee/peg-solitaire.c

Share