#include #include #include #include #include #include #include #include #include #include #include #define MASTERMIND_PORT 5500 /* Mastermind protocol: There exists requests and responses, all fit on one line and end with a CR LF sequence. Support requests are either the command "quit" or a sequence of MASTERMIND_LEN digits where the digits are in [1, MASTERMIND_COLORS]. The quit command asks for the termination of a session, a sequence of digits is interpreted as an attempt to guess the secret code. Responses consist of a three-digit sequence (response code), followed by a space and a message. Following response codes are supported 200 used for the initial greeting 201 response to a guess which was not correct 298 response to the quit command 299 response to a correct guess of the secret codee 400 response to an errorneous request The protocol starts with a greeting by the server (using response code 200). Afterwards each request by the client is responded to by the server. A quit command, a successful guess, or an end of input causes the connection to be closed. */ /* standard configuration is 4, 6, 1 */ #define MASTERMIND_LEN 4 /* length of Mastermind code */ #define MASTERMIND_COLORS 6 /* number of different colors, 9 at max */ #define MASTERMIND_UNIQUE 1 /* are the colors unique? */ typedef struct mastermind { unsigned char code[MASTERMIND_LEN]; /* as digits, starting from '1' */ /* how often is which color present; note that index 0 represents '1' etc. */ unsigned int count[MASTERMIND_COLORS]; } mastermind; /* initialize a game of Mastermind using rand() according to the hardwired configuration above */ void mm_init(mastermind* mm) { for (unsigned int i = 0; i < MASTERMIND_COLORS; ++i) { mm->count[i] = 0; } unsigned long int selected = 0; /* which colors have been selected yet? */ unsigned long int free = MASTERMIND_COLORS; /* # of remaining colors */ for (unsigned int i = 0; i < MASTERMIND_LEN; ++i) { unsigned int rval = rand() % free + 1; if (MASTERMIND_UNIQUE) { unsigned int count = 0; for (unsigned int color = 1; color <= MASTERMIND_COLORS; ++color) { if (((1 << color) & selected) == 0 && ++count == rval) { selected |= 1 << color; rval = color; --free; break; } } } mm->code[i] = rval + '0'; mm->count[rval - 1]++; } } /* evaluate a guess by returning the number of black and white pins */ void mm_check(mastermind* mm, char code[MASTERMIND_LEN], unsigned int* black_ptr, unsigned int* white_ptr) { unsigned int black = 0; unsigned int white = 0; /* we work with counts here to support the case that MASTERMIND_UNIQUE = 0 */ unsigned int count[MASTERMIND_COLORS]; for (unsigned int i = 0; i < MASTERMIND_COLORS; ++i) { count[i] = mm->count[i]; } /* count exact matches first */ for (unsigned int i = 0; i < MASTERMIND_LEN; ++i) { unsigned int color_index = code[i] - '1'; if (mm->code[i] == code[i]) { /* exact match */ ++black; --count[color_index]; } } /* count misplaced but correct colors that are not yet accounted for */ if (black < MASTERMIND_LEN) { for (unsigned int i = 0; i < MASTERMIND_LEN; ++i) { unsigned int color_index = code[i] - '1'; if (mm->code[i] != code[i] && count[color_index] > 0) { /* misplaced color which was not yet counted */ ++white; --count[color_index]; } } } *black_ptr = black; *white_ptr = white; } /* send return message with the given code */ int send_msg(outbuf* out, unsigned int code, char* message) { stralloc msg = {0}; stralloc_catulong0(&msg, code, 3); stralloc_cats(&msg, " "); stralloc_cats(&msg, message); stralloc_cats(&msg, "\r\n"); outbuf_write(out, msg.s, msg.len); return outbuf_flush(out); } /* send final message in response to the quit command that reveals the secret code */ int send_bye(outbuf* out, unsigned int code, mastermind* mm) { stralloc msg = {0}; stralloc_catulong0(&msg, code, 3); stralloc_cats(&msg, " Bye, the secret code was: "); stralloc_catb(&msg, (const char*) mm->code, MASTERMIND_LEN); stralloc_cats(&msg, "\r\n"); outbuf_write(out, msg.s, msg.len); return outbuf_flush(out); } /* send response with the status of the last guess */ int send_status(outbuf* out, unsigned int code, unsigned int black, unsigned int white) { stralloc msg = {0}; stralloc_catulong0(&msg, code, 3); stralloc_cats(&msg, " "); stralloc_catulong0(&msg, black, 0); stralloc_cats(&msg, " "); stralloc_catulong0(&msg, white, 0); stralloc_cats(&msg, " (black/white)"); stralloc_cats(&msg, "\r\n"); outbuf_write(out, msg.s, msg.len); return outbuf_flush(out); } /* run a session for a game of Mastermind */ void mm_session(int fd, int argc, char** argv) { // setup input and outbuf buffers, print greeting inbuf in = {fd}; outbuf out = {fd}; if (!send_msg(&out, 200, "Welcome to Mastermind!")) return; // initialize Mastermind game srand(getpid() ^ time(0)); mastermind mm = {{0}}; mm_init(&mm); // process next command stralloc cmd = {0}; for(;;) { if (!inbuf_sareadline(&in, &cmd)) return; // strip CR, if present if (cmd.len > 0 && cmd.s[cmd.len - 1] == '\r') --cmd.len; // honor quit command if (strncmp(cmd.s, "quit", 4) == 0) break; if (cmd.len != MASTERMIND_LEN) { if (!send_msg(&out, 400, "Unknown command or invalid code length.")) return; continue; } // check input code for validity unsigned int set = 0; bool ok = true; for (int i = 0; ok && i < MASTERMIND_LEN; ++i) { unsigned int code = cmd.s[i]; if (code <= '0' || code >= '0' + MASTERMIND_COLORS) { if (!send_msg(&out, 400, "Invalid code.")) return; ok = false; } if (MASTERMIND_UNIQUE) { code -= '0'; if ((1 << code) & set) { if (!send_msg(&out, 400, "Each digit must be unique.")) return; ok = false; } set |= 1 << code; } } if (!ok) continue; // evaluate input code unsigned int black; unsigned int white; mm_check(&mm, cmd.s, &black, &white); if (black == MASTERMIND_LEN) { send_msg(&out, 299, "That was correct. Congratulations!"); return; } if (!send_status(&out, 201, black, white)) return; } send_bye(&out, 298, &mm); } int main(int argc, char** argv) { run_service(MASTERMIND_PORT, mm_session, argc, argv); }