{"id":13293,"date":"2026-01-19T03:00:00","date_gmt":"2026-01-19T08:00:00","guid":{"rendered":"https:\/\/www.both.org\/?p=13293"},"modified":"2026-01-07T16:32:15","modified_gmt":"2026-01-07T21:32:15","slug":"writing-a-fun-turn-based-game","status":"publish","type":"post","link":"http:\/\/www.both.org\/?p=13293","title":{"rendered":"Writing a fun turn-based game"},"content":{"rendered":"<div class=\"pld-like-dislike-wrap pld-template-1\">\r\n    <div class=\"pld-like-wrap  pld-common-wrap\">\r\n    <a href=\"javascript:void(0)\" class=\"pld-like-trigger pld-like-dislike-trigger  \" title=\"\" data-post-id=\"13293\" data-trigger-type=\"like\" data-restriction=\"cookie\" data-already-liked=\"0\">\r\n                        <i class=\"fas fa-thumbs-up\"><\/i>\r\n                <\/a>\r\n    <span class=\"pld-like-count-wrap pld-count-wrap\">    <\/span>\r\n<\/div><\/div>\n<p>When I need a distraction, I make a little program to amuse myself. Today, I wrote a simple &#8220;code-breaker&#8221; game.<\/p>\n\n\n\n<p>This game is similar to the <a href=\"https:\/\/en.wikipedia.org\/wiki\/Bulls_and_cows\" target=\"_blank\" rel=\"noreferrer noopener\">Bulls and Cows<\/a> game, or the Mastermind board game. An early version of Unix included a version of Bulls and Cows, where you try to guess a series of four random digits, nonrepeating. After each guess, the computer tells you how many digits are correct, or correct but in the wrong position. For example, if the secret code was 4930 and you guessed 1234, the computer would respond with &#8220;1 bull and 1 cow: the bull is 3 and the cow is 4&#8221; because the 3 is correct <em>and in the right place<\/em> and the 4 appears in the secret code <em>but not at that position<\/em>.<\/p>\n\n\n\n<p>I wanted to play a similar game, but I made the hints easier to follow. The user has just five attempts to guess a secret code of five nonrepeating digits. For each guess, the program prints my code, and for each digit indicates <code>!<\/code> if that digit is correct <em>and in the right place<\/em>, <code>?<\/code> if the digit appears in the secret <em>but not at that position<\/em>, and <code>x<\/code> if the number doesn&#8217;t appear in the secret at all. It turns out that five guesses is challenging but not unreasonable.<\/p>\n\n\n\n<p>Here&#8217;s how you can write your own version of this &#8220;code-breaker&#8221; game.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"making-a-secret-code\">Making a secret code<\/h2>\n\n\n\n<p>The core part of the game involves reading input from the user and comparing it to a secret value. To generate a random string of nonrepeating digits, I first started with the numbers 0 to 9, and &#8220;shuffled&#8221; them to make a random order.<\/p>\n\n\n\n<p>One way to get random numbers is with the <strong>getrandom<\/strong> system call, which queries the Linux kernel to fill random bits into a variable. This is a much better method than the C standard library function <strong>rand<\/strong>, which only generates <em>pseudorandom<\/em> values. Here&#8217;s a test program to shuffle a string of digits into a random order:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>#include &lt;stdio.h&gt;\n#include &lt;sys\/random.h&gt;\n\nvoid shuffle(char *list, int len)\n{\n    char c;\n    unsigned int n;\n\n    for (int i = 0; i &lt; len; i++) {\n        getrandom(&amp;n, sizeof(unsigned int), GRND_NONBLOCK);\n        n %= len;\n\n        c = list&#91;n];\n        list&#91;n] = list&#91;i];\n        list&#91;i] = c;\n    }\n}\n\nint main()\n{\n    char secret&#91;] = \"1234567890\";\n\n    puts(secret);\n    shuffle(secret, 10);\n    puts(secret);\n\n    return 0;\n}<\/code><\/pre>\n\n\n\n<p>In the <strong>shuffle<\/strong> function, I&#8217;ve stored the random bits in an unsigned variable so that the random bits will always be positive. Then I use that value in a <em>modulo<\/em> (<code>%<\/code>) operation to get a number between 0 and <code>len-1<\/code>.<\/p>\n\n\n\n<p>Save this test program as <code>shuffle.c<\/code> and compile it:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>$ gcc -o shuffle shuffle.c<\/code><\/pre>\n\n\n\n<p>When I run the program, I can see it print out the original list of digits, then the shuffled list of digits:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>1234567890\n5413702869<\/code><\/pre>\n\n\n\n<p>With this method, I effectively have created a random order of digits. If I use only the first five digits, I have a secret code of five nonrepeating numbers, which I can use in my game.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"read-guesses\">Read guesses<\/h2>\n\n\n\n<p>Reading input from the user is a tricky business. If my program prompts the user to enter some text, my program needs to have enough memory to store the full line. This is where a lot of programmers might create the conditions for a buffer &#8220;overflow,&#8221; where the user enters more data than the program can save into a variable.<\/p>\n\n\n\n<p>For example, my &#8220;code-breaker&#8221; game only uses five digits, you might assume that I only need to make room for five characters of input. But what if the user enters &#8220;123456&#8221; as their guess? That&#8217;s longer than the five characters that are reserved for input.<\/p>\n\n\n\n<p>Instead, I can use the <strong>getline<\/strong> function to read input. This is a flexible method to read data from the user, because it automatically reserves more memory to store all of the text. To use <strong>getline<\/strong>, start by defining a <em>pointer<\/em> to a string variable, and another variable that stores the size. You can allocate some memory to start with, but if you set the pointer to <code>NULL<\/code> and the size to zero then <strong>getline<\/strong> will do the rest to automatically allocate memory as it goes.<\/p>\n\n\n\n<p>Let&#8217;s demonstrate with a sample program that just reads a string from the user and prints it back to the screen. Note that <strong>getline<\/strong> returns the number of characters that it read, or -1 if it reached the end of the input (such as the end of a file).<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>#include &lt;stdio.h&gt;\n#include &lt;stdlib.h&gt;\n\nint main()\n{\n    char *s = NULL;\n    size_t len = 0;\n\n    if (getline(&amp;s, &amp;len, stdin) == -1) {\n        puts(\"(end)\");\n    }\n    else {\n        fputs(s, stdout);\n    }\n\n    free(s);\n    return 0;\n}<\/code><\/pre>\n\n\n\n<p>This program uses the variable <strong>s<\/strong> for the string, which has its size stored in <strong>len<\/strong>. Note that <strong>s<\/strong> starts with a <code>NULL<\/code> value, and <strong>len<\/strong> with zero. That means <strong>getline<\/strong> will allocate some default amount of memory to start. You should release the memory with the <strong>free<\/strong> function before you end the program, which I&#8217;ve done here.<\/p>\n\n\n\n<p>If I save my sample program as <code>input.c<\/code> and compile it, then run it, I can type in some sample text and the program will print it back to me.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Hello world\nHello world<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"comparing-a-guess\">Comparing a guess<\/h2>\n\n\n\n<p>One other component we need for the &#8220;code-breaking&#8221; game is a method to compare the user&#8217;s guess with the secret value. To do this, I like to first print the user&#8217;s guess on a line by itself, then print another line to provide the hints.<\/p>\n\n\n\n<p>To show the user&#8217;s guess, I could just print the full string. But the game only uses a five digit code; if the user entered a longer string, I don&#8217;t want to compare the extra characters in their guess. Similarly, if the user&#8217;s guess was too short, I don&#8217;t need to compare the secret values with text was wasn&#8217;t entered. That means when I display the guess, I only want to limit it to the first five characters, and keep track of how long the user&#8217;s guess was.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>int compare(char *guess, char *secret, int ncompar)\n{\n    int len = ncompar;\n    int correct = 0;\n\n    \/* show guess *\/\n\n    putchar('\\t');\n    for (int i = 0; i &lt; ncompar; i++) {\n        if ((len == ncompar) &amp;&amp; (strnchr(\"\\0\\n\", 2, guess&#91;i]) &gt;= 0)) {\n            len = i;\n        }\n\n        if (i &lt; len) {\n            putchar(guess&#91;i]);\n        }\n        else {\n            putchar(' ');              \/* too short, pad with ' ' *\/\n        }\n    }\n    putchar('\\n');<\/code><\/pre>\n\n\n\n<p>The function starts by assigning the <code>len<\/code> variable to the same value as the <code>ncompar<\/code> parameter, which is the number of characters to compare from the user&#8217;s guess and the secret code. The important stuff is in the <code>for<\/code> loop. When the program encounters a null character (<code>\\0<\/code>) or a new line character (<code>\\n<\/code>) it resets the <code>len<\/code> variable to the current position in the string. When the loop is finished, the <code>len<\/code> variable will be the length of the user&#8217;s input, or the length of the secret code, whichever is greater. That means I can use <code>len<\/code> in another loop to compare just the digits from the user&#8217;s guess:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>    \/* show hints *\/\n\n    putchar('\\t');\n    for (int i = 0; i &lt; len; i++) {\n        if (guess&#91;i] == secret&#91;i]) {\n            putchar('!');              \/* correct *\/\n            correct++;\n        }\n        else if (strnchr(secret, len, guess&#91;i]) &gt;= 0) {\n            putchar('?');              \/* correct, but not correct position *\/\n        }\n        else {\n            putchar('x');              \/* not there *\/\n        }\n    }\n    putchar('\\n');\n\n    return correct;\n}<\/code><\/pre>\n\n\n\n<p>For each digit, the function prints <code>!<\/code> if that digit is correct <em>and in the right place<\/em>, <code>?<\/code> if the digit appears in the secret <em>but not at that position<\/em>, and <code>x<\/code> if the number doesn&#8217;t appear in the secret at all.<\/p>\n\n\n\n<p>To make this function easier to write, I also created a separate &#8220;helper&#8221; function called <strong>strnchr<\/strong> that indicates if a character exists in a longer string. This is similar to the C standard library function <strong>strchr<\/strong> but it only looks at the first <code>n<\/code> characters. Also, my <strong>strnchr<\/strong> function returns an integer value, where <strong>strchr<\/strong> returns a pointer into the string. The function also returns -1 if the character cannot be found in the string.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>int strnchr(char *s, int len, char c)\n{\n    int pos;\n\n    for (pos = 0; pos &lt; len; pos++) {\n        if (c == s&#91;pos]) {\n            return pos;\n        }\n    }\n\n    return -1;\n}<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"putting-it-all-together\">Putting it all together<\/h2>\n\n\n\n<p>With these components, I can now write a full version of the game. After generating a secret code, my program reads guesses from the user, then compares each guess to the secret code. For each guess, the program provides hints for correct and incorrect digits from the secret code. If the user can guess the secret code within five guesses, the program prints a &#8220;That&#8217;s right&#8221; message. Otherwise, the program prints different messages depending on how close the user was to the secret value.<\/p>\n\n\n\n<p>Here&#8217;s the complete program, with extra comments:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>#include &lt;stdio.h&gt;\n#include &lt;stdlib.h&gt;\n#include &lt;sys\/random.h&gt;\n\nvoid shuffle(char *list, int len)\n{\n    char c;\n    unsigned int n;\n\n    \/* shuffle the items in the list. this isn't a very secure shuffle,\n     * but it's only for a simple game so it's okay. *\/\n\n    for (int i = 0; i &lt; len; i++) {\n        getrandom(&amp;n, sizeof(unsigned int), GRND_NONBLOCK);\n        n %= len;\n\n        c = list&#91;n];\n        list&#91;n] = list&#91;i];\n        list&#91;i] = c;\n    }\n}\n\nint strnchr(char *s, int len, char c)\n{\n    int pos;\n\n    \/* find the first occurrence of c in s, for first len chars,\n     * and return the position in the string. *\/\n    for (pos = 0; pos &lt; len; pos++) {\n        if (c == s&#91;pos]) {\n            return pos;\n        }\n    }\n\n    \/* not found, return error *\/\n    return -1;\n}\n\nint compare(char *guess, char *secret, int ncompar)\n{\n    int len = ncompar;\n    int correct = 0;\n\n    \/* show guess *\/\n\n    putchar('\\t');\n    for (int i = 0; i &lt; ncompar; i++) {\n        if ((len == ncompar) &amp;&amp; (strnchr(\"\\0\\n\", 2, guess&#91;i]) &gt;= 0)) {\n            len = i;\n        }\n\n        if (i &lt; len) {\n            putchar(guess&#91;i]);\n        }\n        else {\n            putchar(' ');              \/* too short, pad with ' ' *\/\n        }\n    }\n    putchar('\\n');\n\n    \/* show hints *\/\n\n    putchar('\\t');\n    for (int i = 0; i &lt; len; i++) {\n        if (guess&#91;i] == secret&#91;i]) {\n            putchar('!');              \/* correct *\/\n            correct++;\n        }\n        else if (strnchr(secret, len, guess&#91;i]) &gt;= 0) {\n            putchar('?');              \/* correct, but not correct position *\/\n        }\n        else {\n            putchar('x');              \/* not there *\/\n        }\n    }\n    putchar('\\n');\n\n    return correct;\n}\n\nint main()\n{\n    char secret&#91;] = \"1234567890\";\n    char *guess;\n    size_t size;\n    int turns = 5, found;\n\n    \/* print instructions *\/\n\n    puts(\"You have five turns to guess a 5-digit number, non-repeating.\");\n    puts(\"After each guess, I'll tell you what you got right and wrong,\");\n    puts(\"so you can make your next guess.\");\n\n    \/* let the user guess the secret number *\/\n\n    shuffle(secret, 10);\n\n    do {\n        printf(\"%d:\", turns);          \/* prompt *\/\n\n        if (getline(&amp;guess, &amp;size, stdin) == -1) {\n            turns = 0;\n        }\n        else {\n            found = compare(guess, secret, 5);\n        }\n    } while ((--turns &gt; 0) &amp;&amp; (found &lt; 5));\n\n    \/* give feedback *\/\n\n    if (found == 5) {\n        puts(\"That's right!\");\n    }\n    else if (found &gt;= 3) {\n        puts(\"You almost had it\");\n    }\n    else if (found &gt;= 1) {\n        puts(\"You were getting there\");\n    }\n    else {\n        puts(\"Way off\");\n    }\n\n    free(guess);\n    return 0;\n}<\/code><\/pre>\n\n\n\n<p>Save this as <code>codebrk.c<\/code> and compile it. The game is quite fun to play. It turns out that five guesses is challenging but reasonable. I usually start my guess with <code>12345<\/code>:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>$ .\/codebrk \nYou have five turns to guess a 5-digit number, non-repeating.\nAfter each guess, I'll tell you what you got right and wrong,\nso you can make your next guess.\n5:12345     \n    12345\n    xxx??<\/code><\/pre>\n\n\n\n<p>After my first guess, I know that the secret has the digits 4 and 5, but not in the last two positions. The numbers 1, 2, and 3 do not appear at all in the secret code. So I might update my guess to put 4 and 5 at the start, and guess a few other digits:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>4:45678\n    45678\n    ???!x<\/code><\/pre>\n\n\n\n<p>With this guess, I know that 7 is in the correct position. and that 4, 5, and 6 appear in the secret but not in those positions. The number 8 isn&#8217;t in the secret code at all. This is enough information to make a new guess:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>3:64579\n    64579\n    ???!x<\/code><\/pre>\n\n\n\n<p>It doesn&#8217;t feel like I&#8217;m getting closer, but I am. I know that 9 doesn&#8217;t appear in the secret, and the 4, 5, and 6 are still in the wrong positions. I can make another guess:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>2:06475\n    06475\n    ?!!!?<\/code><\/pre>\n\n\n\n<p>And now I have the information I need to solve the puzzle. I know that 6, 4, and 7 are in the right positions, but 0 and 5 are not. There&#8217;s only one possible code that matches this criteria:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>1:56470\n    56470\n    !!!!!\nThat's right!<\/code><\/pre>\n\n\n\n<p>Writing a simple game like this is a fun way to explore programming. Feel free to modify the program to make it your own, such as giving the user more guesses, or making a longer secret code.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>With a bit of programming, you&#8217;ll never get bored.<\/p>\n","protected":false},"author":33,"featured_media":2949,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_lmt_disableupdate":"","_lmt_disable":"","footnotes":""},"categories":[5,150],"tags":[147,152],"class_list":["post-13293","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-linux","category-programming","tag-fun","tag-programming"],"modified_by":"David Both","_links":{"self":[{"href":"http:\/\/www.both.org\/index.php?rest_route=\/wp\/v2\/posts\/13293","targetHints":{"allow":["GET"]}}],"collection":[{"href":"http:\/\/www.both.org\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/www.both.org\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/www.both.org\/index.php?rest_route=\/wp\/v2\/users\/33"}],"replies":[{"embeddable":true,"href":"http:\/\/www.both.org\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=13293"}],"version-history":[{"count":3,"href":"http:\/\/www.both.org\/index.php?rest_route=\/wp\/v2\/posts\/13293\/revisions"}],"predecessor-version":[{"id":13296,"href":"http:\/\/www.both.org\/index.php?rest_route=\/wp\/v2\/posts\/13293\/revisions\/13296"}],"wp:featuredmedia":[{"embeddable":true,"href":"http:\/\/www.both.org\/index.php?rest_route=\/wp\/v2\/media\/2949"}],"wp:attachment":[{"href":"http:\/\/www.both.org\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=13293"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/www.both.org\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=13293"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/www.both.org\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=13293"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}