pachi_py/pachi/tactics/nakade.c (101 lines of code) (raw):

#include <assert.h> #include <stdio.h> #include <stdlib.h> #define DEBUG #include "board.h" #include "debug.h" #include "move.h" #include "tactics/nakade.h" static inline int nakade_area(struct board *b, coord_t around, enum stone color, coord_t *area) { /* First, examine the nakade area. For sure, it must be at most * six points. And it must be within color group(s). */ #define NAKADE_MAX 6 int area_n = 0; area[area_n++] = around; for (int i = 0; i < area_n; i++) { foreach_neighbor(b, area[i], { if (board_at(b, c) == stone_other(color)) return -1; if (board_at(b, c) == S_NONE) { bool dup = false; for (int j = 0; j < area_n; j++) if (c == area[j]) { dup = true; break; } if (dup) continue; if (area_n >= NAKADE_MAX) { /* Too large nakade area. */ return -1; } area[area_n++] = c; } }); } return area_n; } static inline void get_neighbors(struct board *b, coord_t *area, int area_n, int *neighbors, int *ptbynei) { /* We also collect adjecency information - how many neighbors * we have for each area point, and histogram of this. This helps * us verify the appropriate bulkiness of the shape. */ memset(neighbors, 0, area_n * sizeof(int)); for (int i = 0; i < area_n; i++) { for (int j = i + 1; j < area_n; j++) if (coord_is_adjecent(area[i], area[j], b)) { ptbynei[neighbors[i]]--; neighbors[i]++; ptbynei[neighbors[i]]++; ptbynei[neighbors[j]]--; neighbors[j]++; ptbynei[neighbors[j]]++; } } } static inline coord_t nakade_point_(coord_t *area, int area_n, int *neighbors, int *ptbynei) { /* For each given neighbor count, arbitrary one coordinate * featuring that. */ coord_t coordbynei[9]; for (int i = 0; i < area_n; i++) coordbynei[neighbors[i]] = area[i]; switch (area_n) { case 1: return pass; case 2: return pass; case 3: assert(ptbynei[2] == 1); return coordbynei[2]; // middle point case 4: if (ptbynei[3] != 1) return pass; // long line, L shape, or square return coordbynei[3]; // tetris four case 5: if (ptbynei[3] == 1 && ptbynei[1] == 1) return coordbynei[3]; // bulky five if (ptbynei[4] == 1) return coordbynei[4]; // cross five return pass; // long line case 6: if (ptbynei[4] == 1 && ptbynei[2] == 3) return coordbynei[4]; // rabbity six return pass; // anything else default: assert(0); } return 0; /* NOTREACHED */ } coord_t nakade_point(struct board *b, coord_t around, enum stone color) { assert(board_at(b, around) == S_NONE); coord_t area[NAKADE_MAX]; int area_n = 0; area_n = nakade_area(b, around, color, area); if (area_n == -1) return pass; int neighbors[area_n]; int ptbynei[9] = {area_n, 0}; get_neighbors(b, area, area_n, neighbors, ptbynei); return nakade_point_(area, area_n, neighbors, ptbynei); } bool nakade_dead_shape(struct board *b, coord_t around, enum stone color) { assert(board_at(b, around) == S_NONE); coord_t area[NAKADE_MAX]; int area_n = 0; area_n = nakade_area(b, around, color, area); if (area_n == -1) return false; if (area_n <= 3) return true; int neighbors[area_n]; int ptbynei[9] = {area_n, 0}; get_neighbors(b, area, area_n, neighbors, ptbynei); if (area_n == 4 && ptbynei[2] == 4) // square 4 return true; /* nakade_point() should be able to deal with the rest ... */ coord_t nakade = nakade_point_(area, area_n, neighbors, ptbynei); return nakade != pass; }