For more details about this guide, see:
http://www.lulu.com/shop/andrew-johnson/c-here-programming-in-c-in-linux-and-raspberry-pi/paperback/product-23190178.html (comb bound A4 version)
Also on Amazon (US Letter Paperback)
https://www.amazon.co.uk/Here-Programming-Linux-Raspberry-Pi/dp/154696794X
Kindle version is also available.
/****************************************************************************
* File: masterrmind.c
* Date: 13.05.2017
* Author: A.D. Johnson
* Version: 1.0
* Description: Plays the game of "Mastermind." Uses (nearly all) standard
* `C' so should be portable to other systems.
*
****************************************************************************/
/****************************************************************************
This is a game where a sequence of numbers is set up - a "code" (normally by
a human, but we'll get the computer to do it), and the "codebreaker" tries to
guess the code as quickly as possible. He makes guesses by placing them on a
board. The "codesetter" then "marks" the guess according to certain rules:-
If, in the guess, there is a certain number which is also in the code, but
it is in a DIFFERENT position, a WHITE peg is awarded.
If, in the guess, there is a certain number which is also in the code, but
it is in the SAME position, a BLACK peg is awarded.
Only 1 white or black peg per correct digit is awarded.
Example
If the code was "1234" and the guess was "4255", one black peg (for the "2")
and one white peg (for the "4") would be awarded.
******************************************************************************/
#include <stdio.h> /* Standard I/O - printf, scanf etc. */
#include <string.h> /* strcpy, strcmp etc. */
#include <stdlib.h> /* For rand() and srand(). */
#include <ctype.h> /* For "toupper" - set to upper case.*/
#include <time.h> /* For "time" function (used with "srand" below) */
#include <termios.h> /* Linux System File */
/* Change this if you want more guesses in the game. */
#define MAX_GUESSES 10
/* Global data - the arrays used for the guess and code digits. */
char guess_digits[5];
char code_digits[5];
/* Function pre-declarations. */
void draw_board(void);
void generate_code(void);
void read_in_guess(int);
void display_guess_on_board(int);
int mark_guess(int);
/*************************************************************************
* Function: gotoxy
* Parameters: column - X, row - Y
* Description: Moves printing position to specified screen/window location
*
* Returns: Nothing
**************************************************************************/
void gotoxy(int x, int y)
{
// This uses what's called an ANSI escape sequence to move the cursor
// to a particular location in the window.
printf("\033[%d;%dH", y, x);
}
/*************************************************************************
* Function: clrscr
* Parameters: none
* Description: Clears the "screen" (console printing area)
*
* Returns: Nothing
**************************************************************************/
void clrscr()
{
// This uses what's called an ANSI escape sequence to clear the
// viewing portion of the terminal window.
printf ("\033c");
}
/*************************************************************************
* Function: getch()
* Parameters: none
* Description: Waits for a single key to be pressed, rather than needing
"Enter" or "Return" to be pressed - hence the jiggery pokery
* Returns: The key code that was pressed.
**************************************************************************/
char getch(void)
{
int c=0;
struct termios org_opts, new_opts;
int res=0;
//----- store old IO settings in a structure -----------
res=tcgetattr(fileno(stdin), &org_opts);
//Keep a copy:
new_opts = org_opts;
//Update the bits we're interested in
new_opts.c_lflag &= ~(ICANON | ECHO | ECHOE | ECHOK | ECHONL | ECHOPRT | ECHOKE | ICRNL);
//Put the settings into the system.
tcsetattr(fileno(stdin), TCSANOW, &new_opts);
//Check for a keypress
c=getchar();
//------ restore old settings ---------
res=tcsetattr(fileno(stdin), TCSANOW, &org_opts);
//assert(res==0);
return(c);
}
/*************************************************************************
* Function: main
* Parameters: none
* Description: The main entry point to the program
* Returns: Nothing.
**************************************************************************/
int main()
{
/* Data for "main". */
int continue_game,guess_no, guess_correct;
char response_entered;
/* Start a loop in which we will play the game. */
do
{
/* Clear the screen. */
clrscr();
/* Call the function to draw the board. */
draw_board();
/* Generate the code - in "code_digits". */
generate_code();
/* Now loop for 10 guesses. */
for (guess_no=1; guess_no <= MAX_GUESSES; guess_no++)
{
/* Read in the guess - pass the current guess number. */
read_in_guess(guess_no);
/* Put the guess on the board. */
display_guess_on_board(guess_no);
/* Mark the guess. */
guess_correct = mark_guess(guess_no);
/* If we guessed correctly, break out of the "for" loop. */
if (guess_correct == 1)
{
break;
}
}
/* When we get to here, we will either have run out of goes, or
"guess_correct" will have been set to 1 - the guess has been
marked and found to be correct. */
/* Move cursor ready to print out game result. */
gotoxy(30,7);
/* Check if we guessed it! */
if (guess_correct == 1)
{
printf ("*** You Win! ***");
}
else
{
printf ("*** You have failed! ***");
}
gotoxy (30,9);
/* Print out the code. */
printf ("The code was %s...", code_digits);
/* Now ask if the user wants another game. */
printf ("Another Game (enter Y or N)?");
response_entered = getch();
/* Use "toupper" to convert it to upper case. */
if (toupper(response_entered) == 'Y')
{
continue_game = 1;
}
else
{
continue_game = 0;
}
}
while (continue_game == 1);
/* Print out a closing message... */
gotoxy (30,11);
printf ("*** Bye bye... ***\n\n");
}
/*************************************************************************
* Function: draw_board
* Parameters: none
* Description: Draw a representation of the board in ASCII chars
* Returns: Nothing.
**************************************************************************/
void draw_board(void)
{
int i;
char board_end[] = "+----+----+\n";
char board_middle[]= "| | |\n";
/* Set screen position to top left. */
gotoxy(0,0);
/* Print out the board. */
printf ("%s",board_end);
for (i=0; i < MAX_GUESSES; i++)
{
printf ("%s",board_middle);
}
printf ("%s",board_end);
/* Print out a title. */
gotoxy(30, 1);
printf (" * * * M A S T E R M I N D * * *");
}
/*************************************************************************
* Function: generate_code
* Parameters: none
* Description: Put a random code into "code_digits" array of chars
* Returns: Nothing.
**************************************************************************/
void generate_code(void)
{
int the_code;
/* Set up the random number generator to give us a truly random value. */
srand (time(NULL));
/* Get a 4 digit random number. */
the_code = rand() % 10000;
/* Put this number into a string, in the right form. */
sprintf (code_digits, "%04d", the_code);
}
/*************************************************************************
* Function: read_in_guess
* Parameters: Guess number in game
* Description: read in the guess from the keyboard.
* Returns: Nothing.
**************************************************************************/
void read_in_guess(int guess_no)
{
int i;
char key_entered;
/* Move cursor back to right place. */
gotoxy(30,4);
/* Print out a prompt. */
printf ("Enter guess %d: \b\b\b\b", guess_no);
/* Read in 4 digits. */
for (i=0; i < 4; i++)
{
do
{
/* Get the key pressed. */
key_entered = getch();
}
/* Check the key pressed and loop if it's not valid. */
while ((key_entered < '0') || (key_entered > '9'));
/* Print out the key pressed. */
printf ("%c", key_entered);
/* Put the key in the guess array in the right place - "i". */
guess_digits[i] = key_entered;
}
/* Set the end of "guess_digits" to be 0 - this is not really
necessary, but we do it just to be sure. */
guess_digits[4] = 0;
}
/*************************************************************************
* Function: display_guess_on_board
* Parameters: Guess number in game
* Description: Display the entered guess in the correct place on board
* Returns: Nothing.
**************************************************************************/
void display_guess_on_board(int guess_no)
{
/* Move the cursor. */
gotoxy (2, guess_no + 1);
/* Print the guess. */
printf ("%s", guess_digits);
}
/*************************************************************************
* Function: mark_guess
* Parameters: Guess number in game.
* Description: mark the guess according to the rules and display result
on the board.
This is the most complicated function of the game but the
things we do in the function itself are quite simple.
We have to think carefully how we implement the rules of the
Black/white marking scheme, or we will not generate the correct
Results.
* Returns: 1 if the guess was correct (4 blacks).
**************************************************************************/
int mark_guess(int guess_no)
{
char guess_copy[5], code_copy[5];
int no_of_blacks, no_of_whites, i, j;
/* Set the number of blacks and whites. */
no_of_blacks = no_of_whites = 0;
/* Make a copy of the guess and the code so we can muck about with them. */
strcpy (guess_copy, guess_digits);
strcpy (code_copy, code_digits);
/* Do the blacks. */
for (i = 0; i < 4; i++)
{
/* Check if the corresponding digits of guess and code are the same. */
if (guess_copy[i] == code_copy[i])
{
/* Yes, so "knock out" this digit for when we come to do the whites. */
guess_copy[i] = '*';
code_copy[i] = '#';
/* Count the blacks. */
no_of_blacks ++;
}
}
/* Count the whites. */
for (i = 0; i < 4; i++)
{
/* We will compare each digit of the guess with each digit of the code. */
for (j = 0; j < 4; j++)
{
/* Here, we use the "i" loop for "guess", and the "j" for "code". */
if (guess_copy[i] == code_copy[j])
{
/* Make these digits in the guess and code copies "non-digits" so
that they don't get compared again. */
guess_copy[i] = '*';
code_copy[j] = '#';
/* Count the whites. */
no_of_whites ++;
}
}
}
/* Move print cursor to correct place on board display. */
gotoxy (7, guess_no + 1);
/* Print out the blacks. */
for (i = 0; i < no_of_blacks; i++)
{
printf ("%c", 'B');
}
/* Print out the whites. */
for (i = 0; i < no_of_whites; i++)
{
printf ("%c", 'W');
}
/* If we counted 4 blacks, we want to return 1, otherwise return 0. */
if (no_of_blacks == 4)
{
/* The code has been guessed. */
return (1);
}
else
{
return (0);
}
}
/************************* THE END!!! ****************************