5 * Simple mastermind game
7 * (c) 2006 Mark Wooding
10 /*----- Licensing notice --------------------------------------------------*
12 * This file is part of mm: a simple Mastermind game.
14 * mm is free software; you can redistribute it and/or modify
15 * it under the terms of the GNU General Public License as published by
16 * the Free Software Foundation; either version 2 of the License, or
17 * (at your option) any later version.
19 * mm is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU General Public License for more details.
24 * You should have received a copy of the GNU General Public License
25 * along with mm; if not, write to the Free Software Foundation,
26 * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
29 /*----- Header files ------------------------------------------------------*/
37 #include <mLib/alloc.h>
38 #include <mLib/mdwopt.h>
39 #include <mLib/quis.h>
40 #include <mLib/report.h>
43 /*----- Data structures ---------------------------------------------------*/
47 * The symbols which make up the code to be guessed.
50 typedef unsigned char dig;
52 /* --- The game parameters --- */
55 dig k; /* Number of symbols in the code */
56 dig n; /* Number of distinct symbols */
59 /*----- Rating guesses ----------------------------------------------------*/
61 /* --- Algorithm --- *
63 * Rating guesses efficiently is quite important.
65 * The rate context structure contains a copy of the game parameters, and
66 * three arrays, @v@, @s@ and @t@ allocated from the same @malloc@ed block:
68 * * %$v_i$% counts occurrences of the symbol %$i$% in the code.
69 * * %$s$% is a copy of the code.
70 * * %$t$% is temporary work space for the rating function.
72 * The rating function works by taking a pass over the guess, computing a
73 * count table %$v'$%; but for each guess symbol which matches the
74 * correspondng code symbol, decrementing the count %$v_i$% for that symbol
75 * (in a temporary copy of the table %$v$%). The black count is then the
76 * number of such matches, and the white count is given by
78 * %$w = \displaystyle \sum_{0<i\le n} \min(v_i, v'_i).$%
80 * Thus, the work is %$O(k + n)$%, rather than the %$O(k^2)$% for a
81 * %%na\"\i{}ve%% implementation.
84 typedef struct ratectx {
91 static ratectx *rate_alloc(const mm *m)
97 v = xmalloc((3 * m->n + m->k) * sizeof(dig));
105 static void rate_init(ratectx *r, const dig *s)
109 memset(r->v, 0, r->m.n * sizeof(dig));
110 for (i = 0; i < r->m.k; i++)
112 memcpy(r->s, s, r->m.k * sizeof(dig));
115 static ratectx *rate_new(const mm *m, const dig *s)
117 ratectx *r = rate_alloc(m);
123 static void rate(const ratectx *r, const dig *g, unsigned *b, unsigned *w)
126 unsigned k = r->m.k, n = r->m.n;
130 unsigned bb = 0, ww = 0;
132 memset(v, 0, n * sizeof(dig));
133 memcpy(vv, r->v, n * sizeof(dig));
134 for (i = 0; i < k; i++) {
142 for (i = 0; i < n; i++)
143 ww += v[i] < vv[i] ? v[i] : vv[i];
148 static void rate_free(ratectx *r) { xfree(r->v); DESTROY(r); }
150 /*----- Computer player ---------------------------------------------------*/
152 /* --- About the algorithms --- *
154 * At each stage, we attampt to choose the guess which will give us the most
155 * information, regardless of the outcome. For each guess candidate, we
156 * count the remaining possible codes for each outcome, and choose the
157 * candidate with the least square sum. There are wrinkles.
159 * Firstly the number of possible guesses is large, and the number of
160 * possible codes is huge too; and our algorithm takes time proportional to
161 * the product of the two. However, a symbol we've never tried before is as
162 * good as any other, so we can narrow the list of candidate guesses by
163 * considering only %%\emph{prototypes}%% where we use only the smallest
164 * untried symbol at any point to represent introducing any new symbol. The
165 * number of initial prototypes is quite small. For the four-symbol game,
166 * they are 0000, 0001, 0011, 0012, 0111, 0112, 0122, and 0123.
168 * Secondly, when the number of possible codes become small, we want to bias
169 * the guess selection algorithm towards possible codes (so that we win if
170 * we're lucky). Since the algorithm chooses the first guess with the lowest
171 * sum-of-squares value, we simply run through the possible codes before
172 * enumerating the prototype guesses.
176 mm m; /* Game parameters */
177 dig *s; /* n^k * k */ /* Remaining guesses */
178 size_t ns; /* Number of remaining guesses */
179 dig *bg; /* k */ /* Current best guess */
180 dig *t; /* k */ /* Scratch-space for prototype */
181 double bmax; /* Best guess least-squares score */
182 dig x, bx; /* Next unused symbol index */
183 size_t *v; /* (k + 1)^2 */ /* Bin counts for least-squares */
184 ratectx *r; /* Rate context for search */
187 static void print_guess(const mm *m, const dig *d)
189 unsigned k = m->k, i;
191 for (i = 0; i < k; i++) {
197 static void dofep(cpc *c, void (*fn)(cpc *c, const dig *g, unsigned x),
198 unsigned k, unsigned n, unsigned i, unsigned x)
206 for (j = 0; j < x; j++) {
208 dofep(c, fn, k, n, i + 1, x);
212 dofep(c, fn, k, n, i + 1, x + 1);
217 static void foreach_proto(cpc *c, void (*fn)(cpc *c,
221 unsigned k = c->m.k, n = c->m.n;
223 dofep(c, fn, k, n, 0, c->x);
226 static void try_guess(cpc *c, const dig *g, unsigned x)
237 memset(v, 0, (k + 1) * (k + 1) * sizeof(size_t));
238 for (i = c->ns, s = c->s; i; i--, s += k) {
239 rate(c->r, s, &b, &w);
240 v[b * (k + 1) + w]++;
243 for (i = (k + 1) * (k + 1), vp = v; i; i--, vp++)
244 max += (double)*vp * (double)*vp;
245 if (c->bmax < 0 || max < c->bmax) {
246 memcpy(c->bg, g, k * sizeof(dig));
252 static void best_guess(cpc *c)
260 for (i = c->ns, s = c->s; i; i--, s += k)
261 try_guess(c, s, c->x);
263 foreach_proto(c, try_guess);
267 static void filter_guesses(cpc *c, const dig *g, unsigned b, unsigned w)
276 for (i = c->ns, s = ss = c->s; i; i--, s += k) {
277 rate(c->r, s, &bb, &ww);
278 if (b == bb && w == ww) {
279 memmove(ss, s, k * sizeof(dig));
283 c->ns = (ss - c->s) / k;
286 static size_t ipow(size_t b, size_t x)
298 static void all_guesses(dig **ss, unsigned k, unsigned n,
299 unsigned i, const dig *b)
307 for (j = 0; j < n; j++) {
310 memcpy(*ss, b, i * sizeof(dig));
312 all_guesses(ss, k, n, i + 1, s);
316 #define THINK(what, how) do { \
318 fputs(what "...", stdout); \
323 printf(" done (%.2fs)\n", (_t1 - _t0)/(double)CLOCKS_PER_SEC); \
326 static cpc *cpc_new(const mm *m)
328 cpc *c = CREATE(cpc);
330 c->ns = ipow(c->m.n, c->m.k);
331 c->s = xmalloc((c->ns + 2) * c->m.k * sizeof(dig));
332 c->bg = c->s + c->ns * c->m.k;
333 c->t = c->bg + c->m.k;
335 c->v = xmalloc((c->m.k + 1) * (c->m.k + 1) * sizeof(size_t));
336 c->r = rate_alloc(m);
337 THINK("Setting up", {
338 dig *ss = c->s; all_guesses(&ss, c->m.k, c->m.n, 0, 0);
343 static void cpc_free(cpc *c)
351 static void cp_rate(void *r, const dig *g, unsigned *b, unsigned *w)
352 { rate(r, g, b, w); }
354 static const dig *cp_guess(void *cc)
359 printf("Liar! All solutions ruled out.\n");
363 fputs("Done! Solution is ", stdout);
364 print_guess(&c->m, c->s);
368 printf("(Possible solutions remaining = %lu)\n",
369 (unsigned long)c->ns);
373 for (i = c->ns, s = c->s; i; i--, s += c->m.k) {
374 printf(" %2lu: ", (unsigned long)(c->ns - i + 1));
375 print_guess(&c->m, s);
385 static void cp_update(void *cc, const dig *g, unsigned b, unsigned w)
388 fputs("My guess = ", stdout);
389 print_guess(&c->m, g);
390 printf("; rating = %u black, %u white\n", b, w);
392 filter_guesses(c, g, b, w);
396 /*----- Human player ------------------------------------------------------*/
403 static hpc *hpc_new(const mm *m)
405 hpc *h = CREATE(hpc);
406 h->t = xmalloc(m->k * sizeof(dig));
411 static void hpc_free(hpc *h)
417 static void hp_rate(void *mp, const dig *g, unsigned *b, unsigned *w)
420 fputs("Guess = ", stdout);
422 printf("; rating: ");
424 scanf("%u %u", b, w);
427 static const dig *hp_guess(void *hh)
432 fputs("Your guess: ", stdout);
434 for (i = 0; i < h->m.k; i++) {
442 static void hp_update(void *cc, const dig *g, unsigned b, unsigned w)
444 printf("Rating = %u black, %u white\n", b, w);
447 /*----- Solver player -----------------------------------------------------*/
455 static spc *spc_new(const mm *m)
457 spc *s = CREATE(spc);
464 static void spc_free(spc *s)
471 static const dig *sp_guess(void *ss)
480 return (cp_guess(s->c));
482 fputs("Your guess (dot for end): ", stdout);
484 do ch = getchar(); while (isspace(ch));
485 if (!isdigit(ch)) { s->i = 1; goto again; }
487 for (i = 0; i < h->m.k; i++) {
495 static void sp_update(void *ss, const dig *g, unsigned b, unsigned w)
496 { spc *s = ss; cp_update(s->c, g, b, w); }
498 /*----- Main game logic ---------------------------------------------------*/
500 static int play(const mm *m,
501 void (*ratefn)(void *rr, const dig *g,
502 unsigned *b, unsigned *w),
504 const dig *(*guessfn)(void *gg),
505 void (*updatefn)(void *gg, const dig *g,
506 unsigned b, unsigned w),
519 ratefn(rr, g, &b, &w);
522 updatefn(gg, g, b, w);
526 int main(int argc, char *argv[])
530 void (*ratefn)(void *rr, const dig *g, unsigned *b, unsigned *w) = 0;
536 static struct option opt[] = {
537 { "computer", 0, 0, 'C' },
538 { "human", 0, 0, 'H' },
539 { "solver", 0, 0, 'S' },
542 int i = mdwopt(argc, argv, "CHS", opt, 0, 0, 0);
546 case 'C': h = 0; break;
547 case 'H': h = 1; break;
548 case 'S': h = 2; break;
553 if (argc - optind == 0) {
556 } else if (argc - optind < 2)
557 die(1, "bad parameters");
559 m.k = atoi(argv[optind++]);
560 m.n = atoi(argv[optind++]);
561 if (argc - optind >= m.k) {
562 dig *s = xmalloc(m.k * sizeof(dig));
564 for (i = 0; i < m.k; i++)
565 s[i] = atoi(argv[optind++]);
566 rr = rate_new(&m, s);
571 die(1, "bad parameters");
576 hpc *hh = hpc_new(&m);
578 dig *s = xmalloc(m.k * sizeof(dig));
581 for (i = 0; i < m.k; i++)
583 rr = rate_new(&m, s);
587 n = play(&m, ratefn, rr, hp_guess, hp_update, hh);
591 cpc *cc = cpc_new(&m);
593 n = play(&m, ratefn, rr, cp_guess, cp_update, cc);
595 n = play(&m, hp_rate, &m, cp_guess, cp_update, cc);
599 spc *ss = spc_new(&m);
600 n = play(&m, hp_rate, &m, sp_guess, sp_update, ss);
607 printf("Solved in %d guesses\n", n);
613 /*----- That's all, folks -------------------------------------------------*/