Merge pull request #50 from lemonsqueeze/can_countercap
[pachi.git] / uct / plugin / example.c
blob571a366aa97351c36fff354c459a6cd64d6e5882
1 /* This is an example Pachi UCT plugin. */
2 /* This file is released under the same licence conditions as
3 * <uct/plugin.h>. */
5 /* We will add positive priors (1.0) for moves that play in-between
6 * of two different groups of the same color; that is, moves that connect
7 * two groups or the same color or separate two groups of the same color.
8 * This is not a very good prior actually, since it leads to a lot of
9 * useless moves. (Maybe doing this in simulations would be more interesting?)
10 * But it is a simple enough example. :-) */
12 /* Compile the plugin like this:
13 * gcc -Wall -O3 -march=native -Ipachi_source_root -shared -fPIC -o example.so example.c
14 * Then, load it in Pachi by passing plugin=example.so as a parameter.
15 * You can also pass it parameters: plugin=example.so:p1=v1:p2=v2.
16 * The module supports these parameters:
17 * eqex Number of prior'd simulations, overrides Pachi default
18 * selfatari If specified (selfatari or selfatari=1), test for selfatari
19 * before giving the prior
22 #include <stdio.h>
23 #include <stdlib.h>
25 /* The basic plugin interface. */
26 #include "uct/plugin.h"
27 /* The tactical reading tools, for selfatari testing. */
28 #include "tactics/selfatari.h"
30 /* Our context structure. */
31 struct context {
32 int eqex;
33 bool selfatari;
37 void
38 pachi_plugin_prior(void *data, struct tree_node *node, struct prior_map *map, int eqex)
40 struct context *ctx = data;
41 struct board *b = map->b;
42 if (ctx->eqex >= 0)
43 eqex = ctx->eqex; // override Pachi default
45 /* foreach_free_point defines a variable @c corresponding
46 * to our current coordinate. */
47 foreach_free_point(map->b) {
48 if (!map->consider[c])
49 continue;
51 /* We will look at the current point's 4-neighborhood;
52 * we are to set a prior if we spot two different
53 * groups of the same color. */
55 /* First, a shortcut: We keep track of numbers of neighboring
56 * stones. The | is not a typo. */
57 if ((neighbor_count_at(b, c, S_BLACK) | neighbor_count_at(b, c, S_WHITE)) <= 1)
58 continue;
60 /* Keep track of seen groups for each color; at each
61 * point, we will look only at groups with the same color. */
62 int groups[S_MAX] = {0, 0, 0, 0};
64 /* foreach_neighbor goes through all direct neighbors
65 * of a given coordinate defines also its own variable @c
66 * corresponding to the current coordinate. */
67 foreach_neighbor(b, c, {
68 enum stone s = board_at(b, c); // color of stone
69 group_t g = group_at(b, c); // id of a group
70 if (!g) continue; // no group at this coord
72 if (!groups[s]) {
73 /* First time we see a group of this color. */
74 groups[s] = g;
75 continue;
77 if (groups[s] == g) {
78 /* We have already seen this group. */
79 continue;
81 /* We have already seen another group of this color!
82 * We can connect or split. */
83 goto set_prior;
84 });
85 /* If we reach this point, we have not seen any two groups
86 * to connect. */
87 continue;
89 set_prior:
90 /* Check if our move here is not self-atari if the option
91 * is enabled. */
92 if (ctx->selfatari && is_bad_selfatari(b, map->to_play, c))
93 continue;
95 /* Finally record the prior; value is 0.0 (avoid) to 1.0
96 * (strongly favor). eqex is the number of simulations
97 * the value is worth. */
98 add_prior_value(map, c, 1.0, eqex);
99 } foreach_free_point_end;
102 void *
103 pachi_plugin_init(char *arg, struct board *b, int seed)
105 struct context *ctx = calloc(1, sizeof(*ctx));
107 /* Initialize ctx defaults here. */
108 ctx->eqex = -1;
110 /* This is the canonical Pachi arguments parser. You do not strictly
111 * need to decypher it, you can just use it as a boilerplate. */
112 if (arg) {
113 char *optspec, *next = arg;
114 while (*next) {
115 optspec = next;
116 next += strcspn(next, ":");
117 if (*next) { *next++ = 0; } else { *next = 0; }
119 char *optname = optspec;
120 char *optval = strchr(optspec, '=');
121 if (optval) *optval++ = 0;
123 if (!strcasecmp(optname, "eqex") && optval) {
124 /* eqex takes a required integer argument */
125 ctx->eqex = atoi(optval);
127 } else if (!strcasecmp(optname, "selfatari")) {
128 /* selfatari takes an optional integer
129 * argument */
130 ctx->selfatari = !optval || atoi(optval);
132 } else {
133 fprintf(stderr, "example plugin: Invalid argument %s or missing value\n", optname);
134 exit(1);
139 /* Initialize the rest of ctx (depending on arguments) here. */
141 return ctx;
144 void
145 pachi_plugin_done(void *data)
147 struct context *ctx = data;
148 /* No big magic. */
149 free(ctx);