pachi_py/pachi/patternsp.h (56 lines of code) (raw):

#ifndef PACHI_PATTERNSP_H #define PACHI_PATTERNSP_H /* Matching of spatial pattern features. */ #include "board.h" #include "move.h" #include "pattern.h" /* Spatial stone configuration pattern features - like pattern3 handles * 3x3-area, this handles general N-area (where N is distance in * gridcular metric). These routines define the dictionary of spatial * configurations (accessible by zobrist hashes or indices) and related * data structures; eventually, they support the FEAT_SPATIAL pattern * feature implementation in the General Pattern Matcher (pattern.[ch]). */ /* Maximum spatial pattern diameter. */ #define MAX_PATTERN_DIST 7 /* Maximum number of points in spatial pattern (upper bound). * TODO: Better upper bound to save more data. */ #define MAX_PATTERN_AREA (MAX_PATTERN_DIST*MAX_PATTERN_DIST) /* For each encountered configuration of stones, we keep it "spelled out" * in the spatial dictionary records, index them and refer just the indices * in the feature payloads. This achieves several things: * * We can handle patterns of arbitrary length. * * We can recognize isomorphous configurations (color reversions, * rotations) within the dataset. * * We can visualise patterns corresponding to chosen features. * * Thus, it goes like this: * * +----------------+ +----------------+ * | struct pattern | - | struct feature | * +----------------+ | payload id | * +----------------+ * | FEAT_SPATIAL * | * | ,--<--. * | | | * +-----------------------------------------+ * | struct spatial_dict spatials[] hash[] | * +-----------------------------------------+ * | * +----------------+ * | struct spatial | * +----------------+ */ /* Spatial record - single stone configuration. */ struct spatial { /* Gridcular radius of matched pattern. */ unsigned char dist; /* The points; each point is two bits, corresponding * to {enum stone}. Points are ordered in gridcular-defined * spiral from middle to the edge; the dictionary file has * a comment describing the ordering at the top. */ unsigned char points[MAX_PATTERN_AREA / 4]; #define spatial_point_at(s, i) (((s).points[(i) / 4] >> (((i) % 4) * 2)) & 3) }; /* Fill up the spatial record from @m vincinity, up to full distance * given by pattern config. */ struct pattern_config; void spatial_from_board(struct pattern_config *pc, struct spatial *s, struct board *b, struct move *m); /* Compute hash of given spatial pattern. */ hash_t spatial_hash(unsigned int rotation, struct spatial *s); /* Convert given spatial pattern to string. */ char *spatial2str(struct spatial *s); /* Mapping from point sequence to coordinate offsets (to determine * coordinates relative to pattern center). */ struct ptcoord { short x, y; } ptcoords[MAX_PATTERN_AREA]; /* For each radius, starting index in ptcoords[]. */ unsigned int ptind[MAX_PATTERN_DIST + 2]; /* Zobrist hashes used for points in patterns. */ #define PTH__ROTATIONS 8 hash_t pthashes[PTH__ROTATIONS][MAX_PATTERN_AREA][S_MAX]; #define ptcoords_at(x_, y_, c_, b_, j_) \ int x_ = coord_x((c_), (b_)) + ptcoords[j_].x; \ int y_ = coord_y((c_), (b_)) + ptcoords[j_].y; \ if (x_ >= board_size(b_)) x_ = board_size(b_) - 1; else if (x_ < 0) x_ = 0; \ if (y_ >= board_size(b_)) y_ = board_size(b_) - 1; else if (y_ < 0) y_ = 0; /* Spatial dictionary - collection of stone configurations. */ /* Two ways of lookup: (i) by index (ii) by hash of the configuration. */ struct spatial_dict { /* Indexed base store */ unsigned int nspatials; /* Number of records. */ struct spatial *spatials; /* Actual records. */ /* Hashed access; all isomorphous configurations * are also hashed */ #define spatial_hash_bits 26 // ~256mib array #define spatial_hash_mask ((1 << spatial_hash_bits) - 1) /* Maps to spatials[] indices. The hash function * used is zobrist hashing with fixed values. */ uint32_t hash[1 << spatial_hash_bits]; /* Auxiliary counters for statistics. */ int fills, collisions; }; /* Initializes spatial dictionary, pre-loading existing records from * default filename if exists. If will_append is true, it will not * complain about non-existing file and initialize the dictionary anyway. * If hash is true, loaded spatials will be added to the hashtable; * use false if this is to be done later (e.g. by patternprob). */ struct spatial_dict *spatial_dict_init(bool will_append, bool hash); /* Lookup specified spatial pattern in the dictionary; return index * of the pattern. If the pattern is not found, 0 will be returned. */ static unsigned int spatial_dict_get(struct spatial_dict *dict, int dist, hash_t h); /* Store specified spatial pattern in the dictionary if it is not known yet. * Returns pattern id. Note that the pattern is NOT written to the underlying * file automatically. */ unsigned int spatial_dict_put(struct spatial_dict *dict, struct spatial *s, hash_t); /* Readds given rotation of given pattern to the hash. This is useful only * if you want to tweak hash priority of various patterns. */ bool spatial_dict_addh(struct spatial_dict *dict, hash_t hash, unsigned int id); /* Print stats about the hash to stderr. Companion to spatial_dict_addh(). */ void spatial_dict_hashstats(struct spatial_dict *dict); /* Spatial dictionary file manipulation. */ /* Loading routine is not exported, it is called automatically within * spatial_dict_init(). */ /* Default spatial dict filename to use. */ extern const char *spatial_dict_filename; /* Write comment lines describing the dictionary (e.g. point order * in patterns) to given file. */ void spatial_dict_writeinfo(struct spatial_dict *dict, FILE *f); /* Append specified spatial pattern to the given file. */ void spatial_write(struct spatial_dict *dict, struct spatial *s, unsigned int id, FILE *f); static inline unsigned int spatial_dict_get(struct spatial_dict *dict, int dist, hash_t hash) { unsigned int id = dict->hash[hash]; #ifdef DEBUG if (id && dict->spatials[id].dist != dist) { if (DEBUGL(6)) fprintf(stderr, "Collision dist %d vs %d (hash [%d]%"PRIhash")\n", dist, dict->spatials[id].dist, id, hash); return 0; } #endif return id; } #endif