From fb41e14529b7b84d6345bb42617243a99f412608 Mon Sep 17 00:00:00 2001 From: Petr Baudis Date: Sun, 8 Jan 2012 13:18:24 +0100 Subject: [PATCH] Patternplay: New engine playing and evaluating according to pattern database --- Makefile | 4 +- README | 10 +++ pachi.c | 5 ++ patternplay/Makefile | 12 ++++ patternplay/patternplay.c | 163 ++++++++++++++++++++++++++++++++++++++++++++++ patternplay/patternplay.h | 8 +++ 6 files changed, 200 insertions(+), 2 deletions(-) create mode 100644 patternplay/Makefile create mode 100644 patternplay/patternplay.c create mode 100644 patternplay/patternplay.h diff --git a/Makefile b/Makefile index a19dc71..b6b45ab 100644 --- a/Makefile +++ b/Makefile @@ -92,11 +92,11 @@ INCLUDES=-I. OBJS=board.o gtp.o move.o ownermap.o pattern3.o pattern.o patternsp.o patternprob.o playout.o probdist.o random.o stone.o timeinfo.o network.o fbook.o -SUBDIRS=random replay patternscan joseki montecarlo uct uct/policy playout tactics t-unit distributed +SUBDIRS=random replay patternscan patternplay joseki montecarlo uct uct/policy playout tactics t-unit distributed all: all-recursive pachi -LOCALLIBS=random/random.a replay/replay.a patternscan/patternscan.a joseki/joseki.a montecarlo/montecarlo.a uct/uct.a uct/policy/uctpolicy.a playout/playout.a tactics/tactics.a t-unit/test.a distributed/distributed.a +LOCALLIBS=random/random.a replay/replay.a patternscan/patternscan.a patternplay/patternplay.a joseki/joseki.a montecarlo/montecarlo.a uct/uct.a uct/policy/uctpolicy.a playout/playout.a tactics/tactics.a t-unit/test.a distributed/distributed.a $(LOCALLIBS): all-recursive @ pachi: $(OBJS) pachi.o $(LOCALLIBS) diff --git a/README b/README index 88539d8..02ef041 100644 --- a/README +++ b/README @@ -87,6 +87,8 @@ Other special engines are also provided: distributed/distributed.c should provide all the guidance * a simple "replay" engine that will simply play moves according to the playout policy suggestions +* a simple "patternplay" engine that will play moves according to the + learned patterns * few other purely for development usage @@ -141,6 +143,14 @@ To achieve the latter, note the number of move at the situation you want to evaluate and run the `tools/sgf-ratemove.sh` script. See the comment on top of the script about its usage. +Pattern Move Hinting +~~~~~~~~~~~~~~~~~~~~ + +Pachi can show instantenous pattern-based move suggestions very much +like for example Moyo Go Studio (though of course without a GUI). +You can use the Move Ranking method above (tools/sgf-ratemove.sh), +but pass it an extra parameter '-e patternplay'. + Framework --------- diff --git a/pachi.c b/pachi.c index 1491de2..8f5f2bb 100644 --- a/pachi.c +++ b/pachi.c @@ -14,6 +14,7 @@ #include "montecarlo/montecarlo.h" #include "random/random.h" #include "patternscan/patternscan.h" +#include "patternplay/patternplay.h" #include "joseki/joseki.h" #include "t-unit/test.h" #include "uct/uct.h" @@ -34,6 +35,7 @@ enum engine_id { E_RANDOM, E_REPLAY, E_PATTERNSCAN, + E_PATTERNPLAY, E_MONTECARLO, E_UCT, E_DISTRIBUTED, @@ -45,6 +47,7 @@ static struct engine *(*engine_init[E_MAX])(char *arg, struct board *b) = { engine_random_init, engine_replay_init, engine_patternscan_init, + engine_patternplay_init, engine_montecarlo_init, engine_uct_init, engine_distributed_init, @@ -103,6 +106,8 @@ int main(int argc, char *argv[]) engine = E_DISTRIBUTED; } else if (!strcasecmp(optarg, "patternscan")) { engine = E_PATTERNSCAN; + } else if (!strcasecmp(optarg, "patternplay")) { + engine = E_PATTERNPLAY; } else if (!strcasecmp(optarg, "joseki")) { engine = E_JOSEKI; } else { diff --git a/patternplay/Makefile b/patternplay/Makefile new file mode 100644 index 0000000..820884a --- /dev/null +++ b/patternplay/Makefile @@ -0,0 +1,12 @@ +INCLUDES=-I.. +OBJS=patternplay.o + +all: patternplay.a +patternplay.a: $(OBJS) + +clean: + rm -f *.o *.a +clean-profiled: + rm -f *.gcda *.gcno + +-include ../Makefile.lib diff --git a/patternplay/patternplay.c b/patternplay/patternplay.c new file mode 100644 index 0000000..b4dfeff --- /dev/null +++ b/patternplay/patternplay.c @@ -0,0 +1,163 @@ +#include +#include +#include + +#include "board.h" +#include "debug.h" +#include "engine.h" +#include "move.h" +#include "patternplay/patternplay.h" +#include "pattern.h" +#include "patternsp.h" +#include "patternprob.h" +#include "random.h" + + +/* Internal engine state. */ +struct patternplay { + int debug_level; + + struct pattern_config pc; + pattern_spec ps; + struct pattern_pdict *pd; +}; + + +/* Evaluate patterns for all available moves. Stores found patterns + * to pats[b->flen] and normalized probability of each pattern (or NaN + * in case of no match) to probs[b->flen]. */ +static void +rate_moves(struct pattern_config *pc, pattern_spec *ps, struct pattern_pdict *pd, + struct board *b, enum stone color, + struct pattern *pats, floating_t *probs) +{ + /* First pass: Gather probabilities. */ + floating_t total = 0; + for (int f = 0; f < b->flen; f++) { + probs[f] = NAN; + + struct move mo = { .coord = b->f[f], .color = color }; + if (is_pass(mo.coord)) + continue; + if (!board_is_valid_move(b, &mo)) + continue; + + pattern_match(pc, *ps, &pats[f], b, &mo); + floating_t prob = pattern_prob(pd, &pats[f]); + if (!isnan(prob)) { + probs[f] = prob; + total += prob; + } + } + + /* Second pass: Rescale. */ + for (int f = 0; f < b->flen; f++) { + probs[f] /= total; + } +} + +static coord_t * +patternplay_genmove(struct engine *e, struct board *b, struct time_info *ti, enum stone color, bool pass_all_alive) +{ + struct patternplay *pp = e->data; + + struct pattern pats[b->flen]; + floating_t probs[b->flen]; + rate_moves(&pp->pc, &pp->ps, pp->pd, b, color, pats, probs); + + int best = 0; + for (int f = 0; f < b->flen; f++) { + if (pp->debug_level >= 5 && probs[f] > 0) { + char s[256]; pattern2str(s, &pats[f]); + fprintf(stderr, "\t%s: %.3f %s\n", coord2sstr(b->f[f], b), probs[f], s); + } + if (probs[f] > probs[best]) + best = f; + } + + return coord_copy(b->f[best]); +} + +void +patternplay_evaluate(struct engine *e, struct board *b, struct time_info *ti, floating_t *vals, enum stone color) +{ + struct patternplay *pp = e->data; + + struct pattern pats[b->flen]; + rate_moves(&pp->pc, &pp->ps, pp->pd, b, color, pats, vals); + + if (pp->debug_level >= 4) { + for (int f = 0; f < b->flen; f++) { + if (vals[f] > 0) { + char s[256]; pattern2str(s, &pats[f]); + fprintf(stderr, "\t%s: %.3f %s\n", coord2sstr(b->f[f], b), vals[f], s); + } + } + } +} + + +struct patternplay * +patternplay_state_init(char *arg) +{ + struct patternplay *pp = calloc2(1, sizeof(struct patternplay)); + + pp->debug_level = debug_level; + pp->pc = DEFAULT_PATTERN_CONFIG; + pp->pc.spat_dict = spatial_dict_init(false); + memcpy(&pp->ps, PATTERN_SPEC_MATCH_DEFAULT, sizeof(pattern_spec)); + + if (arg) { + char *optspec, *next = arg; + while (*next) { + optspec = next; + next += strcspn(next, ","); + if (*next) { *next++ = 0; } else { *next = 0; } + + char *optname = optspec; + char *optval = strchr(optspec, '='); + if (optval) *optval++ = 0; + + if (!strcasecmp(optname, "debug")) { + if (optval) + pp->debug_level = atoi(optval); + else + pp->debug_level++; + + /* See pattern.h:pattern_config for description and + * pattern.c:DEFAULT_PATTERN_CONFIG for default values + * of the following options. */ + } else if (!strcasecmp(optname, "bdist_max") && optval) { + pp->pc.bdist_max = atoi(optval); + } else if (!strcasecmp(optname, "spat_min") && optval) { + pp->pc.spat_min = atoi(optval); + } else if (!strcasecmp(optname, "spat_max") && optval) { + pp->pc.spat_max = atoi(optval); + } else if (!strcasecmp(optname, "spat_largest")) { + pp->pc.spat_largest = !optval || atoi(optval); + + } else { + fprintf(stderr, "patternplay: Invalid engine argument %s or missing value\n", optname); + exit(EXIT_FAILURE); + } + } + } + + pp->pd = pattern_pdict_init(NULL, &pp->pc); + + return pp; +} + +struct engine * +engine_patternplay_init(char *arg, struct board *b) +{ + struct patternplay *pp = patternplay_state_init(arg); + struct engine *e = calloc2(1, sizeof(struct engine)); + e->name = "PatternPlay Engine"; + e->comment = "I select moves blindly according to learned patterns. I won't pass as long as there is a place on the board where I can play. When we both pass, I will consider all the stones on the board alive."; + e->genmove = patternplay_genmove; + e->evaluate = patternplay_evaluate; + e->data = pp; + + return e; +} diff --git a/patternplay/patternplay.h b/patternplay/patternplay.h new file mode 100644 index 0000000..0fb9b50 --- /dev/null +++ b/patternplay/patternplay.h @@ -0,0 +1,8 @@ +#ifndef PACHI_PATTERNPLAY_PATTERNPLAY_H +#define PACHI_PATTERNPLAY_PATTERNPLAY_H + +#include "engine.h" + +struct engine *engine_patternplay_init(char *arg, struct board *b); + +#endif -- 2.11.4.GIT