{"id":7505,"date":"2024-09-18T03:00:00","date_gmt":"2024-09-18T07:00:00","guid":{"rendered":"https:\/\/www.both.org\/?p=7505"},"modified":"2024-09-10T07:29:11","modified_gmt":"2024-09-10T11:29:11","slug":"write-a-guessing-game-in-ncurses","status":"publish","type":"post","link":"https:\/\/www.both.org\/?p=7505","title":{"rendered":"Write a guessing game in ncurses"},"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=\"7505\" 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>With ncurses, we can control where and how text gets displayed on the terminal. If you explore the ncurses library functions by reading the manual pages (<code>man 3 ncurses<\/code>) you\u2019ll find there are a ton of different ways to display text, including bold text, colors, blinking text, windows, borders, graphic characters, and other features to make your application stand out.<\/p>\n\n\n\n<p>If you\u2019d like to explore a more advanced program that demonstrates a few of these interesting features, here\u2019s a simple \u201cguess the number\u201d game, updated to use ncurses. The program picks a random number in a range, then asks the user to make repeated guesses until they find the secret number. As the user makes their guess, the program lets them know if the guess was too low or too high.<\/p>\n\n\n\n<p>Good programmers take a problem and break it down into \u201csteps\u201d that can be implemented separately. With this idea, let\u2019s start by creating a few \u201cbuilding blocks\u201d for the program, as separate functions:<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"pick-a-random-value\">Pick a random value<\/h2>\n\n\n\n<p>This program limits the possible numbers from 1 to 8. I used the <code>getrandom()<\/code> kernel system call to generate random bits, masked with the number 7 to pick a random number from 0 (binary 0000) to 7 (binary 0111), plus 1 so the final value is in the range 1 to 8.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>int rand8(void)\n{\n    int num;\n\n    getrandom(&amp;num, sizeof(int), GRND_NONBLOCK);\n\n    \/* 7 is binary 0111, so this returns a number from 0 to 7,\n     * add 1 to get a number from 1 to 8 *\/\n    return (num &amp; 7) + 1;\n}<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"get-the-guess\">Get the guess<\/h2>\n\n\n\n<p>Keeping the values to a limited range of single-digit numbers makes it easier to use <code>getch()<\/code> to read a single number from the user. This uses a shortcut by using the ASCII value of the key that was read from the keyboard. Assuming the standard ASCII ordering, we can subtract the <em>character value<\/em> and the <em>character<\/em> <code>0<\/code> to get a number that\u2019s between 1 and 8:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>int get_guess(void)\n{\n    int ch;\n\n    do {\n        ch = getch();\n    } while ((ch &lt; '1') || (ch &gt; '8'));\n\n    \/* turn it into a number by using ascii value *\/\n    return (ch - '0');\n}<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"center-the-title\">Center the title<\/h2>\n\n\n\n<p>By using ncurses, we can add some visual interest. Let\u2019s add a function to display the title at the top of the screen. This is basically a \u201cwrapper\u201d function that uses <code>strlen()<\/code> to get the string length, then uses the <code>COLS<\/code> global variable to center the line:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>void title(const char *s)\n{\n    mvaddstr(0, (COLS \/ 2) - (strlen(s) \/ 2), s);\n}<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"guess-the-number\">Guess the number<\/h2>\n\n\n\n<p>With these functions, we can construct the main part of our number-guessing game. First, the program sets up the terminal for ncurses, then picks a random number from 1 to 8. After displaying a number scale, the program then enters a loop to ask the user for their guess.<\/p>\n\n\n\n<p>As the user makes their guess, the program provides visual feedback. If the guess is too low, the program prints a left square bracket under the number on the screen. If the guess is too high, the game prints a right square bracket. This helps the user to narrow their choice until they guess the correct number:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>#include &lt;curses.h&gt;                    \/* curses *\/\n#include &lt;string.h&gt;                    \/* strlen *\/\n#include &lt;sys\/random.h&gt;                \/* getrandom *\/\n\nint rand8(void)\n{\n    int num;\n\n    getrandom(&amp;num, sizeof(int), GRND_NONBLOCK);\n\n    \/* 7 is binary 0111, so this returns a number from 0 to 7,\n     * add 1 to get a number from 1 to 8 *\/\n    return (num &amp; 7) + 1;\n}\n\nint get_guess(void)\n{\n    int ch;\n\n    do {\n        ch = getch();\n    } while ((ch &lt; '1') || (ch &gt; '8'));\n\n    \/* turn it into a number by using ascii value *\/\n    return (ch - '0');\n}\n\nvoid title(const char *s)\n{\n    mvaddstr(0, (COLS \/ 2) - (strlen(s) \/ 2), s);\n}\n\nint main()\n{\n    int secret, guess;\n\n    secret = rand8();\n\n    initscr();\n    cbreak();\n    noecho();\n\n    clear();\n    title(\"Guess a number from 1 to 8\");\n    mvaddstr(5, 0, \"Make your guess:\");\n    mvaddstr(10, 1, \"12345678\");\n\n    move(15, 0);                       \/* we'll print messages here *\/\n    refresh();\n\n    do {\n        guess = get_guess();\n\n        if (guess &gt; secret) {\n            mvaddch(11, guess, ']');\n            mvaddstr(15, 0, \"too high\");\n        }\n        else if (guess &lt; secret) {\n            mvaddch(11, guess, '&#91;');\n            mvaddstr(15, 0, \"too low \");\n        }\n        refresh();\n    } while (guess != secret);\n\n    mvaddch(11, guess, '*');\n    mvaddstr(15, 0, \"that's right!\");\n    mvaddstr(16, 0, \"press any key to quit..\");\n    refresh();\n\n    getch();\n\n    endwin();\n    return 0;\n}<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"lets-play\">Let\u2019s play!<\/h2>\n\n\n\n<p>Copy this program and compile it for yourself to try it out. Don\u2019t forget that you need to tell GCC to link with the ncurses library:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>$ gcc -o guess guess.c -lncurses<\/code><\/pre>\n\n\n\n<p>In this sample, the secret number was 2. I started by guessing 1 (too low), then 8 (too high), then 4 (too high). My next guess (2) was just right. You can see at each step, the program printed a \u201cbracket\u201d to indicate if my guess was too low or too high, and an asterisk when I guessed the secret number:<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"827\" height=\"638\" src=\"https:\/\/www.both.org\/wp-content\/uploads\/2024\/09\/guess.png\" alt=\"screenshot: guess the number\" class=\"wp-image-7506\"\/><\/figure>\n<\/div>\n\n\n<p>This program hard-codes a few values to keep things simple and easier to understand. For example, program always prints the title on the first line (line <code>0<\/code>), the prompt on line 5, the list of numbers on line 10, and the \u201cbrackets\u201d on line 11. Feedback (\u201ctoo low\u201d or \u201ctoo high\u201d) are printed on line 15. You might also notice that \u201ctoo low\u201d has a space at the end, so the string is the same length as \u201ctoo high\u201d and so will always overwrite the <code>h<\/code> at the end of \u201ctoo high.\u201d<\/p>\n\n\n\n<p>To learn more about ncurses, read the online manual pages in section 3:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>$ man 3 ncurses<\/code><\/pre>\n\n\n\n<p><em>This article is adapted from <a href=\"https:\/\/opensource.com\/article\/21\/8\/guess-number-game-ncurses-linux\">Write a guessing game in ncurses on Linux<\/a> by Jim Hall, and is published with the author&#8217;s permission.<\/em><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Use the flexibility of ncurses to create a guess-the-number game on Linux.<\/p>\n","protected":false},"author":33,"featured_media":2814,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_lmt_disableupdate":"","_lmt_disable":"","footnotes":""},"categories":[98,69,150],"tags":[147,152],"class_list":["post-7505","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-code","category-fun","category-programming","tag-fun","tag-programming"],"modified_by":"David Both","_links":{"self":[{"href":"https:\/\/www.both.org\/index.php?rest_route=\/wp\/v2\/posts\/7505","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.both.org\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.both.org\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.both.org\/index.php?rest_route=\/wp\/v2\/users\/33"}],"replies":[{"embeddable":true,"href":"https:\/\/www.both.org\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=7505"}],"version-history":[{"count":2,"href":"https:\/\/www.both.org\/index.php?rest_route=\/wp\/v2\/posts\/7505\/revisions"}],"predecessor-version":[{"id":7509,"href":"https:\/\/www.both.org\/index.php?rest_route=\/wp\/v2\/posts\/7505\/revisions\/7509"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.both.org\/index.php?rest_route=\/wp\/v2\/media\/2814"}],"wp:attachment":[{"href":"https:\/\/www.both.org\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=7505"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.both.org\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=7505"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.both.org\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=7505"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}