bool CCreature::step_to(u_char aim_x, u_char aim_y) { if (fabs(aim_y - y) <= aim_x - x && trymove(1,0)) return true; if (fabs(aim_x - x) <= aim_y - y && trymove(0,1)) return true; if (y > aim_y && trymove(0,-1)) return true; if (x > aim_x && trymove(-1,0)) return true; return false; }
/* * mvnum is number of move (rel zero) * see if swapped also tested */ static void trymove(int mvnum, int swapped) { int pos; /* position on board */ int rval; /* value of roll */ /* if recursed through all dice values, compare move */ if (mvnum == mvlim) { binsert(bsave()); return; } /* make sure dice in always same order */ if (d0 == swapped) swap; /* choose value for this move */ rval = dice[mvnum != 0]; /* find all legitimate moves */ for (pos = bar; pos != home; pos += cturn) { /* fix order of dice */ if (d0 == swapped) swap; /* break if stuck on bar */ if (board[bar] != 0 && pos != bar) break; /* on to next if not occupied */ if (board[pos] * cturn <= 0) continue; /* set up arrays for move */ p[mvnum] = pos; g[mvnum] = pos + rval * cturn; if (g[mvnum] * cturn >= home) { if (*offptr < 0) break; g[mvnum] = home; } /* try to move */ if (makmove(mvnum)) continue; else trymove(mvnum + 1, 2); /* undo move to try another */ backone(mvnum); } /* swap dice and try again */ if ((!swapped) && D0 != D1) trymove(0, 1); }
/* Remove persistent cache entries which are no longer compatible with * the board. For efficient use of the cache, it's recommended to call * this function once per move, before starting the owl reading. It's * not required for correct operation though. */ static void purge_persistent_cache(struct persistent_cache *cache) { int k; int r; gg_assert(stackp == 0); /* Never do this more than once per move. */ if (cache->last_purge_position_number == position_number) return; else cache->last_purge_position_number = position_number; for (k = 0; k < cache->current_size; k++) { int played_moves = 0; int entry_ok = 1; struct persistent_cache_entry *entry = &(cache->table[k]); if (entry->boardsize != board_size) entry_ok = 0; else { for (r = 0; r < MAX_CACHE_DEPTH; r++) { int apos = entry->stack[r]; int color = entry->move_color[r]; if (apos == 0) break; if (board[apos] == EMPTY && trymove(apos, color, "purge_persistent_cache", 0)) played_moves++; else { entry_ok = 0; break; } } } if (!entry_ok || !verify_stored_board(entry->board)) { /* Move the last entry in the cache here and back up the loop * counter to redo the test at this position in the cache. */ if (0) gprintf("Purging entry %d from cache.\n", k); if (k < cache->current_size - 1) *entry = cache->table[cache->current_size - 1]; k--; cache->current_size--; } else { /* Reduce score here to penalize entries getting old. */ entry->score *= cache->age_factor; } while (played_moves > 0) { popgo(); played_moves--; } } }
/* Remove persistent cache entries which are no longer current. */ void purge_persistent_reading_cache() { int k; int r; static int last_purge_position_number = -1; gg_assert(stackp == 0); /* Never do this more than once per move. */ if (last_purge_position_number == position_number) return; else last_purge_position_number = position_number; for (k = 0; k < persistent_reading_cache_size; k++) { int played_moves = 0; int entry_ok = 1; if (persistent_reading_cache[k].boardsize != board_size) entry_ok = 0; else { for (r = 0; r < MAX_READING_CACHE_DEPTH; r++) { int apos = persistent_reading_cache[k].stack[r]; int color = persistent_reading_cache[k].move_color[r]; if (apos == 0) break; if (board[apos] == EMPTY && trymove(apos, color, "purge_persistent_reading_cache", 0, EMPTY, 0)) played_moves++; else { entry_ok = 0; break; } } } if (!entry_ok || !verify_stored_board(persistent_reading_cache[k].board)) { /* Move the last entry in the cache here and back up the loop * counter to redo the test at this position in the cache. */ if (0) gprintf("Purging entry %d from cache.\n", k); if (k < persistent_reading_cache_size - 1) persistent_reading_cache[k] = persistent_reading_cache[persistent_reading_cache_size - 1]; k--; persistent_reading_cache_size--; } while (played_moves > 0) { popgo(); played_moves--; } } }
void Player::ai( F32 delta, Map &map, vector<Movable*> &movables ) { Movable *box; if( !moving()) { if( restore ) { direction( rdir ); restore = false; } if( engine.input.keys[ Key::Left ].down ) { if( !trypull( West, map, movables)) { direction( West ); if( !engine.input.shift ) { trymove( map, movables ); } } } else if( engine.input.keys[ Key::Right ].down ) { if( !trypull( East, map, movables )) { direction( East ); if( !engine.input.shift ) { trymove( map, movables ); } } } else if( engine.input.keys[ Key::Up ].down ) { if( !trypull( North, map, movables )) { direction( North ); if( !engine.input.shift ) { trymove( map, movables ); } } } else if( engine.input.keys[ Key::Down ].down ) { if( !trypull( South, map, movables )) { direction( South ); if( !engine.input.shift ) { trymove( map, movables ); } } } if( moving() ) { cout << "@ " << x() << "x" << y() << endl; } } }
int msg_pushblock (int n, int msg, int z) { switch (msg) { case msg_update: if (cando (n,objs[n].x,objs[n].y+1, f_playerthru|f_notstair)==(f_playerthru|f_notstair)) { if ((objs[n].yd+=2)>12) objs[n].yd=12; if (trymove(n,objs[n].x+objs[n].xd,objs[n].y+objs[n].yd)!=1) { trymove(n,objs[n].x,((objs[n].y+objs[n].yd-1)&0xfff0)+ (16-kindyl[obj_pushblock])); objs[n].xd=0; }; }; break; case msg_draw: // drawshape (&gamevp,0x1f38,objs[n].x,objs[n].y); break; drawshape (&gamevp,info[stldr1].sh,objs[n].x,objs[n].y); break; case msg_touch: if (z==0) { if (cando (n,objs[n].x+objs[z].xd*8,objs[n].y,f_playerthru)) { objs [n].x+=objs[z].xd*8; }; }; }; return(1); };
bool kmagnetGenerator::placeBlock(QVector<int> &positions, Moves::Move m) { if (positions.isEmpty()) return false; int num=qrand() % positions.size(); num=positions.at(num); m_scene->getCell(num)->setIsFree(false); forbidcells(num,positions, m); if (makingmorepaths) { int posorig=m_scene->getCurrentCell(); if (trymove(m) && checkFinalPosition(finalpos)) { m_scene->getCell(num)->setIsFree(true); } m_scene->setCurrentPosition(posorig); } return true; }
void automovedown(void) { if (shape_buf != SHPBUF_EMPTY) { if (trycreateshape() == 0) { createshape(); } else { gameover(); g_movbuf.shape = -1; g_ghost.shape = -1; } return; } if (trymove(MOVE_DOWN) == 0) { domove(MOVE_DOWN); } else { steadyall(); } }
bool Player::trypull( Direction dir, Map &map, vector<Movable*> &movables ) { Movable *box; if( engine.input.keys[ Key::Space ].down && direction() != dir ) { if(( box = infront( map, movables )) != NULL && (( CHAIR_TYPE <= box->type() && box->type() <= DESK_TYPE && level() > ( box->type() - CHAIR_TYPE )) || ( ZOMBIE_MALE_TYPE <= box->type() && box->type() <= ZOMBIE_FEMALE_TYPE))){ rdir = direction(); restore = true; direction( dir ); trymove( map, movables ); if( moving()) { box->direction( direction()); box->trymove( map, movables ); // test if there is a wall or block between future position if( !box->moving()) { x( rx()); y( ry()); moving( 0 ); } else { if( map( x(), y()).block & block_dir[ rdir ] || map( box->x(), box->y()).block & block_rdir[ rdir ] ) { x( rx()); y( ry()); moving( 0 ); box->moving( 0 ); box->x( box->rx()); box->y( box->ry()); } } } return true; } } return false; }
/* zero if first move */ void move(int okay) { int i; /* index */ int l; /* last man */ l = 0; if (okay) { /* see if comp should double */ if (gvalue < 64 && dlast != cturn && dblgood()) { writel(*Colorptr); dble(); /* double */ /* return if declined */ if (cturn != 1 && cturn != -1) return; } roll(); } race = 0; for (i = 0; i < 26; i++) { if (board[i] < 0) l = i; } for (i = 0; i < l; i++) { if (board[i] > 0) break; } if (i == l) race = 1; /* print roll */ if (tflag) curmove(cturn == -1 ? 18 : 19, 0); writel(*Colorptr); writel(" rolls "); writec(D0 + '0'); writec(' '); writec(D1 + '0'); /* make tty interruptable while thinking */ if (tflag) cline(); fixtty(noech); /* find out how many moves */ mvlim = movallow(); if (mvlim == 0) { writel(" but cannot use it.\n"); nexturn(); fixtty(raw); return; } /* initialize */ for (i = 0; i < 4; i++) cp[i] = cg[i] = 0; /* strategize */ trymove(0, 0); pickmove(); /* print move */ writel(" and moves "); for (i = 0; i < mvlim; i++) { if (i > 0) writec(','); wrint(p[i] = cp[i]); writec('-'); wrint(g[i] = cg[i]); makmove(i); } writec('.'); /* print blots hit */ if (tflag) curmove(20, 0); else writec('\n'); for (i = 0; i < mvlim; i++) if (h[i]) wrhit(g[i]); /* get ready for next move */ nexturn(); if (!okay) { buflush(); sleep(3); } fixtty(raw); /* no more tty interrupt */ }
void moveright(void) { if (trymove(MOVE_RIGHT) == 0) { domove(MOVE_RIGHT); } updateghost(); }
void domove(int okay) { int i; /* index */ int l = 0; /* last man */ bestmove = -9999999.; if (okay) { /* see if comp should double */ if (dflag && gvalue < 64 && dlast != cturn && dblgood()) { addstr(*Colorptr); dble(); /* double */ /* return if declined */ if (cturn != 1 && cturn != -1) return; } roll(); } race = 0; for (i = 0; i < 26; i++) { if (board[i] < 0) l = i; } for (i = 0; i < l; i++) { if (board[i] > 0) break; } if (i == l) race = 1; /* print roll */ move(cturn == -1 ? 18 : 19, 0); printw("%s rolls %d %d", *Colorptr, D0, D1); clrtoeol(); /* find out how many moves */ mvlim = movallow(); if (mvlim == 0) { addstr(" but cannot use it.\n"); nexturn(); return; } /* initialize */ for (i = 0; i < 4; i++) cp[i] = cg[i] = 0; /* strategize */ trymove(0, 0); pickmove(); /* print move */ addstr(" and moves "); for (i = 0; i < mvlim; i++) { if (i > 0) addch(','); printw("%d-%d", p[i] = cp[i], g[i] = cg[i]); makmove(i); } addch('.'); /* print blots hit */ move(20, 0); for (i = 0; i < mvlim; i++) if (h[i]) wrhit(g[i]); /* get ready for next move */ nexturn(); if (!okay) { refresh(); sleep(3); } }
void Player::hit( Movable *hit, Map &map, vector<Movable*> &movables ) { // is this a non-zombie npc? if( hit->type() < ZOMBIE_MALE_TYPE ) { if(hit->_mapbase.movable_id == 202) { engine.push( new StateEvent( engine, engine.data.events[2603] )); engine.push( new StateEvent( engine, engine.data.events[2602] )); engine.push( new StateEvent( engine, engine.data.events[2601] )); } else if (hit->_mapbase.movable_id == 203) { engine.push( new StateEvent( engine, engine.data.events[2607] )); } else if (hit->_mapbase.movable_id == 208) { engine.push( new StateEvent( engine, engine.data.events[2605] )); engine.push( new StateEvent( engine, engine.data.events[2604] )); } hit->type() += 10; hit->base( &(engine.data.movables[ hit->mutable_id() ] )); hit->mapbase().movable_id = hit->mutable_id(); exp( exp() + 125 ); engine.sfx.loadSound(zombiesfx[rand() % 6], zombieSound, false); zombieSound.Play(); // put down blood for( IDX i = 0; i < 4; ++i ) { if( map( x(), y() ).static_id[i] < 100 ) { map( x(), y() ).static_id[i] = blood[ rand() % 3 ]; break; } } } else if(( CHAIR_TYPE <= hit->type() && hit->type() <= DESK_TYPE && !restore && level() > ( hit->type() - CHAIR_TYPE ))) { hit->direction( direction()); hit->trymove( map, movables ); if( hit->moving()) { trymove( map, movables, false ); } } else if( ZOMBIE_MALE_TYPE <= hit->type() && hit->type() <= ZOMBIE_FEMALE_TYPE ) { if( engine.input.keys[ Key::Space ].down && !hit->moving()) { hit->direction( direction()); hit->trymove( map, movables ); if( hit->moving()) { trymove( map, movables, false ); } } else if( engine.input.keys[ Key::A ].down ) { hit->alive() = 0; engine.sfx.loadSound(zombiesfx[rand() % 6], zombieSound, false); zombieSound.Play(); // put down blood for( IDX i = 0; i < 4; ++i ) { if( map( x(), y() ).static_id[i] < 100 ) { map( x(), y() ).static_id[i] = blood[ rand() % 3 ]; break; } } } else { if( !hit->moving()) { trymove( map, movables, false ); hit->direction( rdirection[ direction() ]); hit->trymove( map, movables, false ); } } } else if( hit->type() == PUZZLE_TYPE ) { // hit Puzzle Block changemap( engine.data.puzzles[ hit->mapbase().mutable_id ] ); } else if( hit->type() == EVENT_TYPE ) { // hit Event Block runevent( engine.data.events[ hit->mapbase().mutable_id ] ); hit->alive(false); } }
void unconditional_life(int unconditional_territory[BOARDMAX], int color) { int found_one; int other = OTHER_COLOR(color); int libs[MAXLIBS]; int liberties; int pos; int k, r; int moves_played; int potential_sekis[BOARDMAX]; int none_invincible; /* Initialize unconditional_territory array. */ memset(unconditional_territory, 0, sizeof(unconditional_territory[0]) * BOARDMAX); /* Find isolated two-stone strings which might be involved in the * kind of seki described in the comments. */ memset(potential_sekis, 0, sizeof(potential_sekis)); for (pos = BOARDMIN; pos < BOARDMAX; pos++) { int isolated = 1; int stones[2]; int pos2; if (board[pos] != color || find_origin(pos) != pos || countstones(pos) != 2) continue; findstones(pos, 2, stones); for (k = 0; k < 2 && isolated; k++) { for (r = 0; r < 8 && isolated; r++) { pos2 = stones[k] + delta[r]; if (!ON_BOARD(pos2) || (board[pos2] == color && !same_string(pos, pos2))) isolated = 0; } } if (isolated) { potential_sekis[stones[0]] = 1; potential_sekis[stones[1]] = 1; } } moves_played = capture_non_invincible_strings(color, potential_sekis, &none_invincible); /* If there are no invincible strings, nothing can be unconditionally * settled. */ if (none_invincible) { /* Take back all moves. */ while (moves_played > 0) { popgo(); moves_played--; } return; } /* The strings still remaining except those marked in * potential_sekis[] are uncapturable. Now see which opponent * strings can survive. * * 1. Play opponent stones on all liberties of the unconditionally * alive strings except where illegal. */ for (pos = BOARDMIN; pos < BOARDMAX; pos++) { if (board[pos] != color || potential_sekis[pos] || find_origin(pos) != pos) continue; /* Play as many liberties as we can. */ liberties = findlib(pos, MAXLIBS, libs); for (k = 0; k < liberties; k++) { if (trymove(libs[k], other, "unconditional_life", pos)) moves_played++; } } /* 2. Recursively extend opponent strings in atari, except where this * would be suicide. */ found_one = 1; while (found_one) { /* Nothing found so far in this turn of the loop. */ found_one = 0; for (pos = BOARDMIN; pos < BOARDMAX; pos++) { if (board[pos] != other || countlib(pos) > 1) continue; /* Try to extend the string at (m, n). */ findlib(pos, 1, libs); if (trymove(libs[0], other, "unconditional_life", pos)) { moves_played++; found_one = 1; } } } /* Now see whether there are any significant sekis on the board. */ for (pos = BOARDMIN; pos < BOARDMAX; pos++) { if (!potential_sekis[pos] || board[pos] == EMPTY || find_origin(pos) != pos) continue; for (r = 0; r < 4; r++) { int up = delta[r]; int right = delta[(r + 1) % 4]; int locally_played_moves = 0; if (board[pos + up] != color || board[pos + up + up] != EMPTY || board[pos - up] != EMPTY) continue; for (k = 0; k < 2; k++) { if (k == 1) right = -right; if (board[pos + right] != EMPTY || board[pos + up - right] != EMPTY) continue; if (board[pos - right] == EMPTY && trymove(pos - right, other, "unconditional_life", pos)) locally_played_moves++; if (board[pos + up + right] == EMPTY && trymove(pos + up + right, other, "unconditional_life", pos)) locally_played_moves++; if (board[pos - right] == other && board[pos + up + right] == other && same_string(pos - right, pos + up + right)) { /* This is a critical seki. Extend the string with one stone * in an arbitrary direction to break the seki. */ while (locally_played_moves > 0) { popgo(); locally_played_moves--; } trymove(pos - up, color, "unconditional_life", pos); moves_played++; break; } else { while (locally_played_moves > 0) { popgo(); locally_played_moves--; } } } if (countstones(pos) > 2) break; } } /* Capture the strings involved in potential sekis. */ for (pos = BOARDMIN; pos < BOARDMAX; pos++) { if (!potential_sekis[pos] || board[pos] == EMPTY) continue; /* Play as many liberties as we can. */ liberties = findlib(pos, MAXLIBS, libs); for (k = 0; k < liberties; k++) { if (trymove(libs[k], other, "unconditional_life", pos)) moves_played++; } } for (pos = BOARDMIN; pos < BOARDMAX; pos++) { int apos; int bpos; int aopen, bopen; int alib, blib; if (board[pos] != other || countlib(pos) != 2) continue; findlib(pos, 2, libs); apos = libs[0]; bpos = libs[1]; if (abs(I(apos) - I(bpos)) + abs(J(apos) - J(bpos)) != 1) continue; /* Only two liberties and these are adjacent. Play one. We want * to maximize the number of open liberties. In this particular * situation we can count this with approxlib for the opposite * color. If the number of open liberties is the same, we * maximize the total number of obtained liberties. * Two relevant positions: * * |XXX. * |OOXX |XXXXXXX * |O.OX |OOXOOOX * |..OX |..OO.OX * +---- +------- */ aopen = approxlib(apos, color, 4, NULL); bopen = approxlib(bpos, color, 4, NULL); alib = approxlib(apos, other, 4, NULL); blib = approxlib(bpos, other, 4, NULL); if (aopen > bopen || (aopen == bopen && alib >= blib)) { trymove(apos, other, "unconditional_life", pos); moves_played++; } else { trymove(bpos, other, "unconditional_life", pos); moves_played++; } } /* Identify unconditionally alive stones and unconditional territory. */ for (pos = BOARDMIN; pos < BOARDMAX; pos++) { if (board[pos] == color && !potential_sekis[pos]) { unconditional_territory[pos] = 1; if (find_origin(pos) == pos) { liberties = findlib(pos, MAXLIBS, libs); for (k = 0; k < liberties; k++) unconditional_territory[libs[k]] = 2; } } else if (board[pos] == other && countlib(pos) == 1) { unconditional_territory[pos] = 2; findlib(pos, 1, libs); unconditional_territory[libs[0]] = 2; } } /* Take back all moves. */ while (moves_played > 0) { popgo(); moves_played--; } }
/* Generate a move to definitely settle the position after the game * has been finished. The purpose of this is to robustly determine * life and death status and to distinguish between life in seki and * life with territory. * * The strategy is basically to turn all own living stones into * invincible ones and remove from the board all dead opponent stones. * Stones which cannot be removed, nor turned invincible, are alive in * seki. * * If do_capture_dead_stones is 0, opponent stones are not necessarily * removed from the board. This happens if they become unconditionally * dead anyway. * * Moves are generated in the following order of priority: * 0. Play edge liberties in certain positions. This is not really * necessary, but often it can simplify the tactical and strategical * reading substantially, making subsequent moves faster to generate. * 1. Capture an opponent string in atari and adjacent to own * invincible string. Moves leading to ko or snapback are excluded. * 2. Extend an invincible string to a liberty of an opponent string. * 3. Connect a non-invincible string to an invincible string. * 4. Extend an invincible string towards an opponent string or an own * non-invincible string. * 5. Split a big eyespace of an alive own dragon without invincible * strings into smaller pieces. * 6. Play a liberty of a dead opponent dragon. * * Steps 2--4 are interleaved to try to optimize the efficiency of the * moves. In step 5 too, efforts are made to play efficient moves. By * efficient we here mean moves which are effectively settling the * position and simplify the tactical and strategical reading for * subsequent moves. * * Steps 1--4 are guaranteed to be completely safe. Step 0 and 5 * should also be risk-free. Step 6 on the other hand definitely * isn't. Consider for example this position: * * .XXXXX. * XXOOOXX * XOO.OOX * XOXXXOX * XO.XXOX * ------- * * In order to remove the O stones, it is necessary to play on one of * the inner liberties, but one of them lets O live. Thus we have to * check carefully for blunders at this step. * * Update: Step 0 is only safe against blunders if care is taken not * to get into a shortage of liberties. * Step 5 also has some risks. Consider this position: * * |XXXXX. * |OOOOXX * |..O.OX * |OX*OOX * +------ * * Playing at * allows X to make seki. * * IMPORTANT RESTRICTION: * Before calling this function it is mandatory to call genmove() or * genmove_conservative(). For this function to be meaningful, the * genmove() call should return pass. */ int aftermath_genmove(int *aftermath_move, int color, int under_control[BOARDMAX], int do_capture_dead_stones) { int k; int other = OTHER_COLOR(color); int distance[BOARDMAX]; int score[BOARDMAX]; float owl_hotspot[BOARDMAX]; float reading_hotspot[BOARDMAX]; int dragons[BOARDMAX]; int something_found; int closest_opponent = NO_MOVE; int closest_own = NO_MOVE; int d; int move = NO_MOVE; int pos = NO_MOVE; int best_score; int best_scoring_move; owl_hotspots(owl_hotspot); reading_hotspots(reading_hotspot); /* As a preparation we compute a distance map to the invincible strings. */ for (pos = BOARDMIN; pos < BOARDMAX; pos++) { if (!ON_BOARD(pos)) continue; else if (board[pos] == color && worm[pos].invincible) distance[pos] = 0; else if (!do_capture_dead_stones && ((board[pos] == other && worm[pos].unconditional_status == DEAD) || (board[pos] == color && worm[pos].unconditional_status == ALIVE))) distance[pos] = 0; else distance[pos] = -1; } d = 0; do { something_found = 0; for (pos = BOARDMIN; pos < BOARDMAX; pos++) { if (ON_BOARD(pos) && distance[pos] == -1) { for (k = 0; k < 4; k++) { int pos2 = pos + delta[k]; if (!ON_BOARD(pos2)) continue; if ((d == 0 || board[pos2] == EMPTY) && distance[pos2] == d) { if (d > 0 && board[pos] == other) { distance[pos] = d + 1; if (closest_opponent == NO_MOVE) closest_opponent = pos; } else if (d > 0 && board[pos] == color) { distance[pos] = d + 1; if (closest_own == NO_MOVE) closest_own = pos; } else if (board[pos] == EMPTY) { distance[pos] = d + 1; something_found = 1; } break; } } } } d++; } while (something_found); if (under_control) { for (pos = BOARDMIN; pos < BOARDMAX; pos++) { if (!ON_BOARD(pos)) continue; else if (distance[pos] == -1) under_control[pos] = 0; else under_control[pos] = 1; } } if (debug & DEBUG_AFTERMATH) { int m, n; for (m = 0; m < board_size; m++) { for (n = 0; n < board_size; n++) { pos = POS(m, n); if (distance[pos] > 0) fprintf(stderr, "%2d", distance[pos]); else if (distance[pos] == 0) { if (board[pos] == WHITE) gprintf(" o"); else if (board[pos] == BLACK) gprintf(" x"); else gprintf(" ?"); } else { if (board[pos] == WHITE) gprintf(" O"); else if (board[pos] == BLACK) gprintf(" X"); else gprintf(" ."); } } gprintf("\n"); } gprintf("Closest opponent %1m", closest_opponent); if (closest_opponent != NO_MOVE) gprintf(", distance %d\n", distance[closest_opponent]); else gprintf("\n"); gprintf("Closest own %1m", closest_own); if (closest_own != NO_MOVE) gprintf(", distance %d\n", distance[closest_own]); else gprintf("\n"); } /* Case 0. This is a special measure to avoid a certain kind of * tactical reading inefficiency. * * Here we play on edge liberties in the configuration * * XO. * .*. * --- * * to stop X from "leaking" out along the edge. Sometimes this can * save huge amounts of tactical reading for later moves. */ best_scoring_move = NO_MOVE; best_score = 5; for (pos = BOARDMIN; pos < BOARDMAX; pos++) { int libs; if (board[pos] != EMPTY || distance[pos] == 0) continue; libs = approxlib(pos, color, 3, NULL); if (libs < 3) continue; if (is_self_atari(pos, other)) continue; for (k = 0; k < 4; k++) { int dir = delta[k]; int right = delta[(k+1)%4]; if (!ON_BOARD(pos - dir) && board[pos + dir] == color && board[pos + dir + right] == other && board[pos + dir - right] == other && (libs > countlib(pos + dir) || (libs > 4 && libs == countlib(pos + dir))) && (DRAGON2(pos + dir).safety == INVINCIBLE || DRAGON2(pos + dir).safety == STRONGLY_ALIVE)) { int this_score = 20 * (owl_hotspot[pos] + reading_hotspot[pos]); if (this_score > best_score) { best_score = this_score; best_scoring_move = pos; } } } } if (best_scoring_move != NO_MOVE && safe_move(best_scoring_move, color) == WIN) { *aftermath_move = best_scoring_move; DEBUG(DEBUG_AFTERMATH, "Closing edge at %1m\n", best_scoring_move); return 1; } /* Case 1. */ for (pos = BOARDMIN; pos < BOARDMAX; pos++) { int lib; if (board[pos] == other && worm[pos].unconditional_status != DEAD && countlib(pos) == 1 && ((ON_BOARD(SOUTH(pos)) && distance[SOUTH(pos)] == 0) || (ON_BOARD(WEST(pos)) && distance[WEST(pos)] == 0) || (ON_BOARD(NORTH(pos)) && distance[NORTH(pos)] == 0) || (ON_BOARD(EAST(pos)) && distance[EAST(pos)] == 0))) { findlib(pos, 1, &lib); /* Make sure we don't play into a ko or a (proper) snapback. */ if (countstones(pos) > 1 || !is_self_atari(lib, color)) { *aftermath_move = lib; return 1; } } } /* Cases 2--4. */ if (closest_opponent != NO_MOVE || closest_own != NO_MOVE) { if (closest_own == NO_MOVE) move = closest_opponent; else move = closest_own; /* if we're about to play at distance 1, try to optimize the move. */ if (distance[move] == 2) { char mx[BOARDMAX]; char mark = 0; memset(mx, 0, sizeof(mx)); best_score = 0; best_scoring_move = move; for (pos = BOARDMIN; pos < BOARDMAX; pos++) { int score = 0; int move_ok = 0; if (!ON_BOARD(pos) || distance[pos] != 1) continue; mark++; for (k = 0; k < 4; k++) { int pos2 = pos + delta[k]; if (!ON_BOARD(pos2)) continue; if (distance[pos2] < 1) score--; else if (board[pos2] == EMPTY) score++; else if (mx[pos2] == mark) score--; else { if (board[pos2] == color) { move_ok = 1; score += 7; if (countstones(pos2) > 2) score++; if (countstones(pos2) > 4) score++; if (countlib(pos2) < 4) score++; if (countlib(pos2) < 3) score++; } else { int deltalib = (approxlib(pos, other, MAXLIBS, NULL) - countlib(pos2)); move_ok = 1; score++; if (deltalib >= 0) score++; if (deltalib > 0) score++; } mark_string(pos2, mx, mark); } } if (is_suicide(pos, other)) score -= 3; if (0) gprintf("Score %1m = %d\n", pos, score); if (move_ok && score > best_score) { best_score = score; best_scoring_move = pos; } } move = best_scoring_move; } while (distance[move] > 1) { for (k = 0; k < 4; k++) { int pos2 = move + delta[k]; if (ON_BOARD(pos2) && board[pos2] == EMPTY && distance[pos2] == distance[move] - 1) { move = pos2; break; } } } *aftermath_move = move; return 1; } /* Case 5. * If we reach here, either all strings of a dragon are invincible * or no string is. Next we try to make alive dragons invincible by * splitting big eyes into smaller ones. Our strategy is to search * for an empty vertex with as many eye points as possible adjacent * and with at least one alive but not invincible stone adjacent or * diagonal. */ for (pos = BOARDMIN; pos < BOARDMAX; pos++) { int eyespace_neighbors = 0; int own_neighbors = 0; int own_diagonals = 0; int opponent_dragons = 0; int own_worms = 0; int safety = UNKNOWN; int bonus = 0; int mx[BOARDMAX]; score[pos] = 0; if (board[pos] != EMPTY || distance[pos] != -1) continue; memset(mx, 0, sizeof(mx)); for (k = 0; k < 8; k++) { int pos2 = pos + delta[k]; if (!ON_BOARD(pos2)) continue; if (board[pos2] == EMPTY) { if (k < 4) eyespace_neighbors++; continue; } if (board[pos2] == other) { int origin = dragon[pos2].origin; if (k < 4) { if (dragon[pos2].status == ALIVE) { safety = DEAD; break; } else if (!mx[origin]) { eyespace_neighbors++; opponent_dragons++; } } if (!mx[origin] && dragon[pos2].status == DEAD) { bonus++; if (k < 4 && countlib(pos2) <= 2 && countstones(pos2) >= 3) bonus++; if (k < 4 && countlib(pos2) == 1) bonus += 3; } mx[origin] = 1; } else if (board[pos2] == color) { dragons[pos] = pos2; if (safety == UNKNOWN && dragon[pos2].status == ALIVE) safety = ALIVE; if (DRAGON2(pos2).safety == INVINCIBLE) safety = INVINCIBLE; if (k < 4) { int apos = worm[pos2].origin; if (!mx[apos]) { own_worms++; if (countstones(apos) == 1) bonus += 2; if (countlib(apos) < 6 && approxlib(pos, color, 5, NULL) < countlib(apos)) bonus -= 5; mx[apos] = 1; } if (countlib(apos) <= 2) { int r; int important = 0; int safe_atari = 0; for (r = 0; r < 4; r++) { d = delta[r]; if (!ON_BOARD(apos+d)) continue; if (board[apos+d] == other && dragon[apos+d].status == DEAD) important = 1; else if (board[apos+d] == EMPTY && !is_self_atari(apos+d, other)) safe_atari = 1; } if (approxlib(pos, color, 3, NULL) > 2) { bonus++; if (important) { bonus += 2; if (safe_atari) bonus += 2; } } } own_neighbors++; } else own_diagonals++; } } if (safety == DEAD || safety == UNKNOWN || eyespace_neighbors == 0 || (own_neighbors + own_diagonals) == 0) continue; if (bonus < 0) bonus = 0; score[pos] = 4 * eyespace_neighbors + bonus; if (safety == INVINCIBLE) { score[pos] += own_neighbors; if (own_neighbors < 2) score[pos] += own_diagonals; if (own_worms > 1 && eyespace_neighbors >= 1) score[pos] += 10 + 5 * (own_worms - 2); } else if (eyespace_neighbors > 2) score[pos] += own_diagonals; /* Splitting bonus. */ if (opponent_dragons > 1) score[pos] += 10 * (opponent_dragons - 1); /* Hotspot bonus. */ { int owl_hotspot_bonus = (int) (20.0 * owl_hotspot[pos]); int reading_hotspot_bonus = (int) (20.0 * reading_hotspot[pos]); int hotspot_bonus = owl_hotspot_bonus + reading_hotspot_bonus; /* Don't allow the hotspot bonus to turn a positive score into * a non-positive one. */ if (score[pos] > 0 && score[pos] + hotspot_bonus <= 0) hotspot_bonus = 1 - score[pos]; score[pos] += hotspot_bonus; if (1 && (debug & DEBUG_AFTERMATH)) gprintf("Score %1M = %d (hotspot bonus %d + %d)\n", pos, score[pos], owl_hotspot_bonus, reading_hotspot_bonus); } /* Avoid taking ko. */ if (is_ko(pos, color, NULL)) score[pos] = (score[pos] + 1) / 2; } while (1) { int bb; best_score = 0; move = NO_MOVE; for (pos = BOARDMIN; pos < BOARDMAX; pos++) { if (ON_BOARD(pos) && score[pos] > best_score) { best_score = score[pos]; move = pos; } } if (move == NO_MOVE) break; bb = dragons[move]; if (is_illegal_ko_capture(move, color) || !safe_move(move, color) || (DRAGON2(bb).safety != INVINCIBLE && DRAGON2(bb).safety != STRONGLY_ALIVE && owl_does_defend(move, bb, NULL) != WIN) || (!confirm_safety(move, color, NULL, NULL))) { score[move] = 0; } else { /* If we're getting short of liberties, we must be more careful. * Check that no adjacent string or dragon gets more alive by * the move. */ int libs = approxlib(move, color, 5, NULL); int move_ok = 1; if (libs < 5) { for (k = 0; k < 4; k++) { if (board[move + delta[k]] == color && countlib(move + delta[k]) > libs) break; } if (k < 4) { if (trymove(move, color, "aftermath-B", move + delta[k])) { int adjs[MAXCHAIN]; int neighbors; int r; neighbors = chainlinks(move, adjs); for (r = 0; r < neighbors; r++) { if (worm[adjs[r]].attack_codes[0] != 0 && (find_defense(adjs[r], NULL) > worm[adjs[r]].defense_codes[0])) { DEBUG(DEBUG_AFTERMATH, "Blunder: %1m becomes tactically safer after %1m\n", adjs[r], move); move_ok = 0; } } popgo(); for (r = 0; r < neighbors && move_ok; r++) { if (dragon[adjs[r]].status == DEAD && !owl_does_attack(move, adjs[r], NULL)) { DEBUG(DEBUG_AFTERMATH, "Blunder: %1m becomes more alive after %1m\n", adjs[r], move); move_ok = 0; } } } } } if (!move_ok) score[move] = 0; else { *aftermath_move = move; DEBUG(DEBUG_AFTERMATH, "Splitting eyespace at %1m\n", move); return 1; } } } /* Case 6. * Finally we try to play on liberties of remaining DEAD opponent * dragons, carefully checking against mistakes. */ for (pos = BOARDMIN; pos < BOARDMAX; pos++) { int target; int cc = NO_MOVE; int self_atari_ok = 0; if (board[pos] != EMPTY || distance[pos] != -1) continue; target = NO_MOVE; for (k = 0; k < 4; k++) { int pos2 = pos + delta[k]; if (!ON_BOARD(pos2)) continue; if (board[pos2] == other && dragon[pos2].status != ALIVE && (do_capture_dead_stones || worm[pos2].unconditional_status != DEAD) && DRAGON2(pos2).safety != INESSENTIAL) { target = pos2; break; } } if (target == NO_MOVE) continue; /* At this point, (pos) is a move that potentially may capture * a dead opponent string at (target). */ if (!trymove(pos, color, "aftermath-A", target)) continue; /* It is frequently necessary to sacrifice own stones in order * to force the opponent's stones to be removed from the board, * e.g. by adding stones to fill up a nakade shape. However, we * should only play into a self atari if the sacrificed stones * are classified as INESSENTIAL. Thus it would be ok for O to * try a self atari in this position: * * |OOOO * |XXXO * |..XO * |OOXO * +---- * * but not in this one: * * |XXX.. * |OOXX. * |.OOXX * |XXOOX * |.O.OX * +----- */ self_atari_ok = 1; for (k = 0; k < 4; k++) { if (board[pos + delta[k]] == color && DRAGON2(pos + delta[k]).safety != INESSENTIAL) { self_atari_ok = 0; cc = pos + delta[k]; break; } } /* Copy the potential move to (move). */ move = pos; /* If the move is a self atari, but that isn't okay, try to * recursively find a backfilling move which later makes the * potential move possible. */ if (!self_atari_ok) { while (countlib(pos) == 1) { int lib; findlib(pos, 1, &lib); move = lib; if (!trymove(move, color, "aftermath-B", target)) break; } if (countlib(pos) == 1) move = NO_MOVE; } while (stackp > 0) popgo(); if (move == NO_MOVE) continue; /* Make sure that the potential move really isn't a self * atari. In the case of a move found after backfilling this * could happen (because the backfilling moves happened to * capture some stones). */ if (!self_atari_ok && is_self_atari(move, color)) continue; /* Consult the owl code to determine whether the considered move * really is effective. Blunders should be detected here. */ if (owl_does_attack(move, target, NULL) == WIN) { /* If we have an adjacent own dragon, which is not inessential, * verify that it remains safe. */ if (cc != NO_MOVE && !owl_does_defend(move, cc, NULL)) continue; /* If we don't allow self atari, also call confirm safety to * avoid setting up combination attacks. */ if (!self_atari_ok && !confirm_safety(move, color, NULL, NULL)) continue; *aftermath_move = move; DEBUG(DEBUG_AFTERMATH, "Filling opponent liberty at %1m\n", move); return 1; } } /* Case 7. * In very rare cases it turns out we need yet another pass. An * example is this position: * * |..... * |OOOO. * |XXXO. * |.OXO. * |O.XO. * +----- * * Here the X stones are found tactically dead and therefore the * corner O stones have been amalgamated with the surrounding * stones. Since the previous case only allows sacrificing * INESSENTIAL stones, it fails to take X off the board. * * The solution is to look for tactically attackable opponent stones * that still remain on the board but should be removed. */ for (pos = BOARDMIN; pos < BOARDMAX; pos++) { if (board[pos] == other && (worm[pos].unconditional_status == UNKNOWN || do_capture_dead_stones) && (DRAGON2(pos).safety == DEAD || DRAGON2(pos).safety == TACTICALLY_DEAD) && worm[pos].attack_codes[0] != 0 && !is_illegal_ko_capture(worm[pos].attack_points[0], color)) { *aftermath_move = worm[pos].attack_points[0]; DEBUG(DEBUG_AFTERMATH, "Tactically attack %1m at %1m\n", pos, *aftermath_move); return 1; } } /* No move found. */ return -1; }
/* Capture as many strings of the given color as we can. Played stones * are left on the board and the number of played stones is returned. * Strings marked in the exceptions array are excluded from capturing * attempts. If all non-excepted strings are successfully captured, * *none_invincible is set to one. Set none_invincible to NULL if you * don't need that information. */ static int capture_non_invincible_strings(int color, int exceptions[BOARDMAX], int *none_invincible) { int other = OTHER_COLOR(color); int something_captured = 1; /* To get into the first turn of the loop. */ int string_found = 0; int moves_played = 0; int save_moves; int libs[MAXLIBS]; int liberties; int pos; int k; while (something_captured) { /* Nothing captured so far in this turn of the loop. */ something_captured = 0; /* Is there something left to try to capture? */ string_found = 0; /* Visit all friendly strings on the board. */ for (pos = BOARDMIN; pos < BOARDMAX; pos++) { if (board[pos] != color || find_origin(pos) != pos) continue; if (exceptions && exceptions[pos]) continue; string_found = 1; /* Try to capture the string at pos. */ liberties = findlib(pos, MAXLIBS, libs); save_moves = moves_played; for (k = 0; k < liberties; k++) { if (trymove(libs[k], other, "unconditional_life", pos)) moves_played++; } /* Successful if already captured or a single liberty remains. * Otherwise we must rewind and take back the last batch of moves. */ if (board[pos] == EMPTY) something_captured = 1; else if (findlib(pos, 2, libs) == 1) { /* Need to use tryko as a defense against the extreme case * when the only opponent liberty that is not suicide is an * illegal ko capture, like in this 5x5 position: * +-----+ * |.XO.O| * |XXOO.| * |X.XOO| * |XXOO.| * |.XO.O| * +-----+ */ int success = tryko(libs[0], other, "unconditional_life"); gg_assert(success); moves_played++; something_captured = 1; } else while (moves_played > save_moves) { popgo(); moves_played--; } } } if (none_invincible) *none_invincible = !string_found; return moves_played; }
void kmagnetGenerator::generaterec() { if (checkFinalPosition(finalpos)) { m_scene->resizeScene( (int)m_scene->sceneRect().width(), (int)m_scene->sceneRect().height() );//redraw } else { //try to move to a non visited place int nextmove=qrand() % 4; if (trymove((Moves::Move)(nextmove))) { generaterec(); return; } if (trymove((Moves::Move)((int)(nextmove+1)%4))) { generaterec(); return; } if (trymove((Moves::Move)((int)(nextmove+2)%4))) { generaterec(); return; } if (trymove((Moves::Move)((int)(nextmove+3)%4))) { generaterec(); return; } //if its not possible, place a new block uint current=m_scene->getCurrentCell(); nextmove=qrand() % 4; if (newBlock(current, (Moves::Move)(nextmove))) { generaterec(); return; } if (newBlock(current, (Moves::Move)((int)(nextmove+1)%4))) { generaterec(); return; } if (newBlock(current, (Moves::Move)((int)(nextmove+2)%4))) { generaterec(); return; } if (newBlock(current, (Moves::Move)((int)(nextmove+3)%4))) { generaterec(); return; } //if its not possible, try to go back movements.pop_back(); while (!movements.isEmpty()) { int n=movements.last(); kmagnetCell* c= m_scene->getCell(n); if (c->getIsFree() && !c->getIsFinal()) { m_scene->setCurrentPosition(n); if (checkFinalPosition(finalpos)) return; } movements.pop_back(); } //if its not possible try to place a new ending cell if (tryplacefinal(lastmove)) return; if (tryplacefinal((Moves::Move)((int)(lastmove+1)%4))) return; if (tryplacefinal((Moves::Move)((int)(lastmove+2)%4))) return; if (tryplacefinal((Moves::Move)((int)(lastmove+3)%4))) return; //qDebug() << "GAME OVER !!!!"; } }
void moveleft(void) { if (trymove(MOVE_LEFT) == 0) { domove(MOVE_LEFT); } updateghost(); }
/* zero if first move */ void move(struct move *mm, int okay) { int i; /* index */ int l; /* last man */ l = 0; if (okay) { /* see if comp should double */ if (gvalue < 64 && dlast != cturn && dblgood()) { writel(*Colorptr); dble(); /* double */ /* return if declined */ if (cturn != 1 && cturn != -1) return; } roll(mm); } race = 0; for (i = 0; i < 26; i++) { if (board[i] < 0) l = i; } for (i = 0; i < l; i++) { if (board[i] > 0) break; } if (i == l) race = 1; /* print roll */ if (tflag) curmove(cturn == -1 ? 18 : 19, 0); writel(*Colorptr); writel(" rolls "); writec(mm->D0 + '0'); writec(' '); writec(mm->D1 + '0'); /* make tty interruptable while thinking */ if (tflag) cline(); fixtty(&noech); /* find out how many moves */ mm->mvlim = movallow(mm); if (mm->mvlim == 0) { writel(" but cannot use it.\n"); nexturn(); fixtty(&raw); return; } /* initialize */ for (i = 0; i < 4; i++) cp[i] = cg[i] = 0; /* strategize */ trymove(mm, 0, 0); pickmove(mm); /* print move */ writel(" and moves "); for (i = 0; i < mm->mvlim; i++) { if (i > 0) writec(','); wrint(mm->p[i] = cp[i]); writec('-'); wrint(mm->g[i] = cg[i]); makmove(mm, i); /* * This assertion persuades gcc 4.5 that the loop * doesn't result in signed overflow of i. mvlim * isn't, or at least shouldn't be, changed by makmove * at all. */ assert(mm->mvlim >= 0 && mm->mvlim <= 5); } writec('.'); /* print blots hit */ if (tflag) curmove(20, 0); else writec('\n'); for (i = 0; i < mm->mvlim; i++) if (mm->h[i]) wrhit(mm->g[i]); /* get ready for next move */ nexturn(); if (!okay) { buflush(); sleep(3); } fixtty(&raw); /* no more tty interrupt */ }