void pattern_get(struct pattern_config *pc, struct pattern *p, struct board *b, struct move *m) { p->n = 0; struct feature *f = &p->f[0]; /* TODO: We should match pretty much all of these features * incrementally. */ /* FEAT_SPATIAL */ /* TODO */ assert(!pc->spat_max); /* FEAT_PASS */ if (is_pass(m->coord)) { f->id = FEAT_PASS; f->payload = 0; f->payload |= (b->moves > 0 && is_pass(b->last_move.coord)) << PF_PASS_LASTPASS; p->n++; return; } /* FEAT_CAPTURE */ { foreach_neighbor(b, m->coord, { if (board_at(b, c) != stone_other(m->color)) continue; group_t g = group_at(b, c); if (!g || board_group_info(b, g).libs != 1) continue; /* Capture! */ f->id = FEAT_CAPTURE; f->payload = 0; f->payload |= is_ladder(b, m->coord, g, true, true) << PF_CAPTURE_LADDER; /* TODO: is_ladder() is too conservative in some * very obvious situations, look at complete.gtp. */ /* TODO: PF_CAPTURE_RECAPTURE */ foreach_in_group(b, g) { foreach_neighbor(b, c, { assert(board_at(b, c) != S_NONE || c == m->coord); if (board_at(b, c) != m->color) continue; group_t g = group_at(b, c); if (!g || board_group_info(b, g).libs != 1) continue; /* A neighboring group of ours is in atari. */ f->payload |= 1 << PF_CAPTURE_ATARIDEF; }); } foreach_in_group_end; if (group_is_onestone(b, g) && neighbor_count_at(b, m->coord, stone_other(m->color)) + neighbor_count_at(b, m->coord, S_OFFBOARD) == 4) f->payload |= 1 << PF_CAPTURE_KO; (f++, p->n++); });
bool is_middle_ladder(Board *b, Coord coord, group_t laddered, Stone lcolor) { /* TODO: Remove the redundant parameters. */ assert(group_at(b, laddered)->liberties == 1); Coord last_lib = get_nlibs_of_group(b, laddered, 1, NULL); assert(last_lib == coord); assert(group_at(b, laddered)->color == lcolor); /* If we can move into empty space or do not have enough space * to escape, this is obviously not a ladder. */ if (immediate_liberty_count(b, coord) != 2) { /* if (DEBUGL(5)) fprintf(stderr, "no ladder, wrong free space\n"); */ return false; } /* A fair chance for a ladder. Group in atari, with some but limited * space to escape. Time for the expensive stuff - set up a temporary * board and start selective 2-liberty search. */ Board *bset = (Board *)malloc(BOARD_MAX_SIZE * 2 * sizeof(Board)); struct move_queue ccq = { .moves = 0 }; if (can_countercapture(b, lcolor, laddered, lcolor, &ccq, 0)) { /* We could escape by countercapturing a group. * Investigate. */ assert(ccq.moves > 0); for (unsigned int i = 0; i < ccq.moves; i++) { Board b2; CopyBoard(&b2, b); bool is_ladder = middle_ladder_walk(&b2, bset, laddered, ccq.move[i], lcolor); // board_done_noalloc(&b2); if (!is_ladder) { free(bset); return false; } } } Board b2; CopyBoard(&b2, b); Coord last_lib2 = get_nlibs_of_group(&b2, laddered, 1, NULL); bool is_ladder = middle_ladder_walk(&b2, bset, laddered, last_lib2, lcolor); // board_done_noalloc(&b2); free(bset); return is_ladder; } bool wouldbe_ladder(Board *b, group_t group, Coord escapelib, Coord chaselib, Stone lcolor) { assert(b->_groups[group].liberties == 2); assert(b->_groups[group].color == lcolor); /* if (DEBUGL(6)) fprintf(stderr, "would-be ladder check - does %s %s play out chasing move %s?\n", stone2str(lcolor), coord2sstr(escapelib, b), coord2sstr(chaselib, b)); */ if (!NEIGHBOR8(escapelib, chaselib)) { /* if (DEBUGL(5)) fprintf(stderr, "cannot determine ladder for remote simulated stone\n"); */ return false; } if (neighbor_count_at(b, chaselib, lcolor) != 1 || immediate_liberty_count(b, chaselib) != 2) { /* if (DEBUGL(5)) fprintf(stderr, "overly trivial for a ladder\n"); */ return false; } bool is_ladder = false; Board *bset = (Board *)malloc(BOARD_MAX_SIZE * 2 * sizeof(Board)); Board b2; CopyBoard(&b2, b); GroupId4 ids; if (TryPlay(&b2, X(chaselib), Y(chaselib), OPPONENT(lcolor), &ids)) { Play(&b2, &ids); Coord last_lib2 = get_nlibs_of_group(&b2, group, 1, NULL); is_ladder = middle_ladder_walk(&b2, bset, group, last_lib2, lcolor); } // board_done_noalloc(&b2); free(bset); return is_ladder; }