Пример #1
0
bool
legal_move_board (unsigned short pos, unsigned char color, bool allow_suicide)
{
    /* you can always pass */
    if (pos == PASS_POS)
    {
        return true;
    }

    if (!on_board (pos) || (color != BLACK && color != WHITE))
    {
        return false;
    }

    if (pos == ko_pos && color == current_player)
    {
        return false;
    }

    if (get_point_board (pos) != EMPTY)
    {
        return false;
    }

    /* don't need to save the current state, because it's always empty
       since we tested for that above */
    set_point_board (pos, color);

    /* if we have liberties, it can't be illegal */
    if (get_liberties_board (pos) > 0 ||
        /* if we can capture something, it can't be illegal */
        (get_point_board (NORTH (pos)) == OTHER (color) &&
         !get_liberties_board (NORTH (pos))) ||
        (get_point_board (SOUTH (pos)) == OTHER (color) &&
         !get_liberties_board (SOUTH (pos))) ||
        (get_point_board (EAST (pos)) == OTHER (color) &&
         !get_liberties_board (EAST (pos))) ||
        (get_point_board (WEST (pos)) == OTHER (color) &&
         !get_liberties_board (WEST (pos))) ||
        /* if we're allowed to suicide, only multi-stone suicide is legal
           (no ruleset allows single-stone suicide that I know of) */
        (allow_suicide && (get_point_board (NORTH (pos)) == color ||
                           get_point_board (SOUTH (pos)) == color ||
                           get_point_board (EAST (pos)) == color ||
                           get_point_board (WEST (pos)) == color)))
    {
        /* undo our previous set */
        set_point_board (pos, EMPTY);
        return true;
    }
    else
    {
        /* undo our previous set */
        set_point_board (pos, EMPTY);
        return false;
    }
}
Пример #2
0
static aligned_t update(void *arg)
{
    stencil_t *points = ((update_args_t *)arg)->points;
    size_t i = ((update_args_t *)arg)->i;
    size_t j = ((update_args_t *)arg)->j;
    size_t this_stage = ((update_args_t *)arg)->stage;
    size_t step = ((update_args_t *)arg)->step;

    size_t next_stage_id = next_stage(this_stage);

    // Perform local work
    perform_local_work();
    aligned_t **prev = points->stage[prev_stage(this_stage)];
    aligned_t sum = *(NORTH(prev, i, j)) 
                  + *(WEST(prev, i, j)) 
                  + *(HERE(prev, i, j)) 
                  + *(EAST(prev, i, j)) 
                  + *(SOUTH(prev, i, j));

    // Empty the next stage for this index
    qthread_empty(&points->stage[next_stage_id][i][j]);

    // Update this point
    qthread_writeEF_const(&points->stage[this_stage][i][j], sum/NUM_NEIGHBORS);
    
    if (step < num_timesteps) {
        // Spawn next stage
        update_args_t args = {points, i, j, next_stage_id, step+1};
#ifdef BOUNDARY_SYNC 
        qthread_fork_copyargs_precond(update, &args, sizeof(update_args_t), NULL, NUM_NEIGHBORS, NEIGHBORS(points->stage[this_stage],i,j));
#else
        if (i == 1) {                   // North edge
            if (j == 1)                     // West edge: EAST & SOUTH
                qthread_fork_copyargs_precond(update, &args, sizeof(update_args_t), NULL, 2, EAST(points->stage[this_stage],i,j), SOUTH(points->stage[this_stage],i,j));
            else if (j == points->M-2)      // East edge: WEST & SOUTH
                qthread_fork_copyargs_precond(update, &args, sizeof(update_args_t), NULL, 2, WEST(points->stage[this_stage],i,j), SOUTH(points->stage[this_stage],i,j));
            else                            // Interior: WEST & EAST & SOUTH
                qthread_fork_copyargs_precond(update, &args, sizeof(update_args_t), NULL, 3, WEST(points->stage[this_stage],i,j), EAST(points->stage[this_stage],i,j), SOUTH(points->stage[this_stage],i,j));
        } else if (i == points->N-2) {  // South edge
            if (j == 1)                     // West edge: NORTH & EAST
                qthread_fork_copyargs_precond(update, &args, sizeof(update_args_t), NULL, 2, NORTH(points->stage[this_stage],i,j), EAST(points->stage[this_stage],i,j));
            else if (j == points->M-2)      // East edge: NORTH & WEST
                qthread_fork_copyargs_precond(update, &args, sizeof(update_args_t), NULL, 2, NORTH(points->stage[this_stage],i,j), WEST(points->stage[this_stage],i,j));
            else                            // Interior: NORTH & WEST & EAST
                qthread_fork_copyargs_precond(update, &args, sizeof(update_args_t), NULL, 3, NORTH(points->stage[this_stage],i,j), WEST(points->stage[this_stage],i,j), EAST(points->stage[this_stage],i,j));
        } else {                        // Interior
            if (j == 1)                     // West edge: NORTH & EAST & SOUTH
                qthread_fork_copyargs_precond(update, &args, sizeof(update_args_t), NULL, 3 , NORTH(points->stage[this_stage],i,j), EAST(points->stage[this_stage],i,j), SOUTH(points->stage[this_stage],i,j));
            else if (j == points->M-2)      // East edge: NORTH & WEST & SOUTH
                qthread_fork_copyargs_precond(update, &args, sizeof(update_args_t), NULL, 3, NORTH(points->stage[this_stage],i,j), WEST(points->stage[this_stage],i,j), SOUTH(points->stage[this_stage],i,j));
            else                            // Interior: ALL
                qthread_fork_copyargs_precond(update, &args, sizeof(update_args_t), NULL, 4, NORTH(points->stage[this_stage],i,j), EAST(points->stage[this_stage],i,j), WEST(points->stage[this_stage],i,j), SOUTH(points->stage[this_stage],i,j));
        }
#endif
    }
    else
        qt_feb_barrier_enter(points->barrier);

    return 0;
}
Пример #3
0
int
flood_fill_board (unsigned short pos, unsigned char color)
{
    if (!on_board (pos) || get_point_board (pos) == color)
    {
        return 0;
    }

    empty_stack (&parse_stack);

    int ret_val = 0;

    unsigned char orig_color = get_point_board (pos);

    set_point_board (pos, color);
    ++ret_val;
    push_pos_stack (&parse_stack, pos);

    while (pop_pos_stack (&parse_stack, &pos))
    {
        ret_val += flood_fill_helper (NORTH (pos), orig_color, color);
        ret_val += flood_fill_helper (SOUTH (pos), orig_color, color);
        ret_val += flood_fill_helper (EAST (pos), orig_color, color);
        ret_val += flood_fill_helper (WEST (pos), orig_color, color);
    }

    return ret_val;
}
Пример #4
0
void
move_display (unsigned short pos)
{
    if (!on_board (pos))
    {
        return;
    }

    while ((unsigned) I (pos) >= REAL_MAX_X)
    {
        cursor_pos = EAST (cursor_pos);
        cursor_updated ();
    }
    while ((unsigned) I (pos) < REAL_MIN_X)
    {
        cursor_pos = WEST (cursor_pos);
        cursor_updated ();
    }

    while ((unsigned) J (pos) >= REAL_MAX_Y)
    {
        cursor_pos = SOUTH (cursor_pos);
        cursor_updated ();
    }
    while ((unsigned) J (pos) < REAL_MIN_Y)
    {
        cursor_pos = NORTH (cursor_pos);
        cursor_updated ();
    }
}
Пример #5
0
int
get_liberties_board (unsigned short pos)
{
    if (!on_board (pos) || get_point_board (pos) == EMPTY)
    {
        return -1;
    }

    setup_marks ();

    int ret_val = 0;
    unsigned char orig_color = get_point_board (pos);

    empty_stack (&parse_stack);
    push_pos_stack (&parse_stack, pos);

    /* Since we only ever test for liberties in order to determine
       captures and the like, there's no reason to count any liberties
       higher than 2 (we sometimes need to know if something has 1 liberty
       for dealing with ko) */
    while (pop_pos_stack (&parse_stack, &pos) && ret_val < 2)
    {
        ret_val += get_liberties_helper (NORTH (pos), orig_color);
        ret_val += get_liberties_helper (SOUTH (pos), orig_color);
        ret_val += get_liberties_helper (EAST (pos), orig_color);
        ret_val += get_liberties_helper (WEST (pos), orig_color);
    }

    /* if there's more than two liberties, the stack isn't empty, so empty
       it */
    empty_stack (&parse_stack);

    return ret_val;
}
Пример #6
0
int palObs( size_t n, const char * c,
            char * ident, size_t identlen,
            char * name, size_t namelen,
            double * w, double * p, double * h ) {
  const struct telData telData[] = {
    /* AAT (Observer's Guide)                                            AAT */
    {
      EAST(149,3,57.91),
      SOUTH(31,16,37.34),
      1164E0,
      "AAT",
      "Anglo-Australian 3.9m Telescope"
    },
    /* WHT (Gemini, April 1987)                                       LPO4.2 */
    {
      WEST(17,52,53.9),
      NORTH(28,45,38.1),
      2332E0,
      "LPO4.2",
      "William Herschel 4.2m Telescope"
    },
    /* INT (Gemini, April 1987)                                       LPO2.5 */
    {
      WEST(17,52,39.5),
      NORTH(28,45,43.2),
      2336E0,
      "LPO2.5",
      "Isaac Newton 2.5m Telescope"
    },
    /* JKT (Gemini, April 1987)                                         LPO1 */
    {
      WEST(17,52,41.2),
      NORTH(28,45,39.9),
      2364E0,
      "LPO1",
      "Jacobus Kapteyn 1m Telescope"
    },
    /* Lick 120" (S.L.Allen, private communication, 2002)            LICK120 */
    {
      WEST(121,38,13.689),
      NORTH(37,20,34.931),
      1286E0,
      "LICK120",
      "Lick 120 inch"
    },
    /* MMT 6.5m conversion (MMT Observatory website)                     MMT */
    {
      WEST(110,53,4.4),
      NORTH(31,41,19.6),
      2608E0,
      "MMT",
      "MMT 6.5m, Mt Hopkins"
    },
    /* Victoria B.C. 1.85m (1984 Almanac)                              DAO72 */
    {
      WEST(123,25,1.18),
      NORTH(48,31,11.9),
      238E0,
      "DAO72",
      "DAO Victoria BC 1.85 metre"
    },
    /* Las Campanas (1983 Almanac)                                    DUPONT */
    {
      WEST(70,42,9.),
      SOUTH(29,0,11.),
      2280E0,
      "DUPONT",
      "Du Pont 2.5m Telescope, Las Campanas"
    },
    /* Mt Hopkins 1.5m (1983 Almanac)                               MTHOP1.5 */
    {
      WEST(110,52,39.00),
      NORTH(31,40,51.4),
      2344E0,
      "MTHOP1.5",
      "Mt Hopkins 1.5 metre"
    },
    /* Mt Stromlo 74" (1983 Almanac)                               STROMLO74 */
    {
      EAST(149,0,27.59),
      SOUTH(35,19,14.3),
      767E0,
      "STROMLO74",
      "Mount Stromlo 74 inch"
    },
    /* ANU 2.3m, SSO (Gary Hovey)                                     ANU2.3 */
    {
      EAST(149,3,40.3),
      SOUTH(31,16,24.1),
      1149E0,
      "ANU2.3",
      "Siding Spring 2.3 metre"
    },
    /* Greenbank 140' (1983 Almanac)                                 GBVA140 */
    {
      WEST(79,50,9.61),
      NORTH(38,26,15.4),
      881E0,
      "GBVA140",
      "Greenbank 140 foot"
    },
    /* Cerro Tololo 4m (1982 Almanac)                               TOLOLO4M */
    {
      WEST(70,48,53.6),
      SOUTH(30,9,57.8),
      2235E0,
      "TOLOLO4M",
      "Cerro Tololo 4 metre"
    },
    /* Cerro Tololo 1.5m (1982 Almanac)                           TOLOLO1.5M */
    {
      WEST(70,48,54.5),
      SOUTH(30,9,56.3),
      2225E0,
      "TOLOLO1.5M",
      "Cerro Tololo 1.5 metre"
    },
    /* Tidbinbilla 64m (1982 Almanac)                              TIDBINBLA */
    {
      EAST(148,58,48.20),
      SOUTH(35,24,14.3),
      670E0,
      "TIDBINBLA",
      "Tidbinbilla 64 metre"
    },
    /* Bloemfontein 1.52m (1981 Almanac)                              BLOEMF */
    {
      EAST(26,24,18.),
      SOUTH(29,2,18.),
      1387E0,
      "BLOEMF",
      "Bloemfontein 1.52 metre"
    },
    /* Bosque Alegre 1.54m (1981 Almanac)                         BOSQALEGRE */
    {
      WEST(64,32,48.0),
      SOUTH(31,35,53.),
      1250E0,
      "BOSQALEGRE",
      "Bosque Alegre 1.54 metre"
    },
    /* USNO 61" astrographic reflector, Flagstaff (1981 Almanac)   FLAGSTF61 */
    {
      WEST(111,44,23.6),
      NORTH(35,11,2.5),
      2316E0,
      "FLAGSTF61",
      "USNO 61 inch astrograph, Flagstaff"
    },
    /* Lowell 72" (1981 Almanac)                                    LOWELL72 */
    {
      WEST(111,32,9.3),
      NORTH(35,5,48.6),
      2198E0,
      "LOWELL72",
      "Perkins 72 inch, Lowell"
    },
    /* Harvard 1.55m (1981 Almanac)                                  HARVARD */
    {
      WEST(71,33,29.32),
      NORTH(42,30,19.0),
      185E0,
      "HARVARD",
      "Harvard College Observatory 1.55m"
    },
    /* Okayama 1.88m (1981 Almanac)                                  OKAYAMA */
    {
      EAST(133,35,47.29),
      NORTH(34,34,26.1),
      372E0,
      "OKAYAMA",
      "Okayama 1.88 metre"
    },
    /* Kitt Peak Mayall 4m (1981 Almanac)                            KPNO158 */
    {
      WEST(111,35,57.61),
      NORTH(31,57,50.3),
      2120E0,
      "KPNO158",
      "Kitt Peak 158 inch"
    },
    /* Kitt Peak 90 inch (1981 Almanac)                               KPNO90 */
    {
      WEST(111,35,58.24),
      NORTH(31,57,46.9),
      2071E0,
      "KPNO90",
      "Kitt Peak 90 inch"
    },
    /* Kitt Peak 84 inch (1981 Almanac)                               KPNO84 */
    {
      WEST(111,35,51.56),
      NORTH(31,57,29.2),
      2096E0,
      "KPNO84",
      "Kitt Peak 84 inch"
    },
    /* Kitt Peak 36 foot (1981 Almanac)                             KPNO36FT */
    {
      WEST(111,36,51.12),
      NORTH(31,57,12.1),
      1939E0,
      "KPNO36FT",
      "Kitt Peak 36 foot"
    },
    /* Kottamia 74" (1981 Almanac)                                  KOTTAMIA */
    {
      EAST(31,49,30.),
      NORTH(29,55,54.),
      476E0,
      "KOTTAMIA",
      "Kottamia 74 inch"
    },
    /* La Silla 3.6m (1981 Almanac)                                   ESO3.6 */
    {
      WEST(70,43,36.),
      SOUTH(29,15,36.),
      2428E0,
      "ESO3.6",
      "ESO 3.6 metre"
    },
    /* Mauna Kea 88 inch                                            MAUNAK88 */
    /* (IfA website, Richard Wainscoat) */
    {
      WEST(155,28,9.96),
      NORTH(19,49,22.77),
      4213.6E0,
      "MAUNAK88",
      "Mauna Kea 88 inch"
    },
    /* UKIRT (IfA website, Richard Wainscoat)                          UKIRT */
    {
      WEST(155,28,13.18),
      NORTH(19,49,20.75),
      4198.5E0,
      "UKIRT",
      "UK Infra Red Telescope"
    },
    /* Quebec 1.6m (1981 Almanac)                                  QUEBEC1.6 */
    {
      WEST(71,9,9.7),
      NORTH(45,27,20.6),
      1114E0,
      "QUEBEC1.6",
      "Quebec 1.6 metre"
    },
    /* Mt Ekar 1.82m (1981 Almanac)                                   MTEKAR */
    {
      EAST(11,34,15.),
      NORTH(45,50,48.),
      1365E0,
      "MTEKAR",
      "Mt Ekar 1.82 metre"
    },
    /* Mt Lemmon 60" (1981 Almanac)                               MTLEMMON60 */
    {
      WEST(110,42,16.9),
      NORTH(32,26,33.9),
      2790E0,
      "MTLEMMON60",
      "Mt Lemmon 60 inch"
    },
    /* Mt Locke 2.7m (1981 Almanac)                               MCDONLD2.7 */
    {
      WEST(104,1,17.60),
      NORTH(30,40,17.7),
      2075E0,
      "MCDONLD2.7",
      "McDonald 2.7 metre"
    },
    /* Mt Locke 2.1m (1981 Almanac)                               MCDONLD2.1 */
    {
      WEST(104,1,20.10),
      NORTH(30,40,17.7),
      2075E0,
      "MCDONLD2.1",
      "McDonald 2.1 metre"
    },
    /* Palomar 200" (1981 Almanac)                                PALOMAR200 */
    {
      WEST(116,51,50.),
      NORTH(33,21,22.),
      1706E0,
      "PALOMAR200",
      "Palomar 200 inch"
    },
    /* Palomar 60" (1981 Almanac)                                  PALOMAR60 */
    {
      WEST(116,51,31.),
      NORTH(33,20,56.),
      1706E0,
      "PALOMAR60",
      "Palomar 60 inch"
    },
    /* David Dunlap 74" (1981 Almanac)                              DUNLAP74 */
    {
      WEST(79,25,20.),
      NORTH(43,51,46.),
      244E0,
      "DUNLAP74",
      "David Dunlap 74 inch"
    },
    /* Haute Provence 1.93m (1981 Almanac)                         HPROV1.93 */
    {
      EAST(5,42,46.75),
      NORTH(43,55,53.3),
      665E0,
      "HPROV1.93",
      "Haute Provence 1.93 metre"
    },
    /* Haute Provence 1.52m (1981 Almanac)                         HPROV1.52 */
    {
      EAST(5,42,43.82),
      NORTH(43,56,0.2),
      667E0,
      "HPROV1.52",
      "Haute Provence 1.52 metre"
    },
    /* San Pedro Martir 83" (1981 Almanac)                           SANPM83 */
    {
      WEST(115,27,47.),
      NORTH(31,2,38.),
      2830E0,
      "SANPM83",
      "San Pedro Martir 83 inch"
    },
    /* Sutherland 74" (1981 Almanac)                                  SAAO74 */
    {
      EAST(20,48,44.3),
      SOUTH(32,22,43.4),
      1771E0,
      "SAAO74",
      "Sutherland 74 inch"
    },
    /* Tautenburg 2m (1981 Almanac)                                  TAUTNBG */
    {
      EAST(11,42,45.),
      NORTH(50,58,51.),
      331E0,
      "TAUTNBG",
      "Tautenburg 2 metre"
    },
    /* Catalina 61" (1981 Almanac)                                CATALINA61 */
    {
      WEST(110,43,55.1),
      NORTH(32,25,0.7),
      2510E0,
      "CATALINA61",
      "Catalina 61 inch"
    },
    /* Steward 90" (1981 Almanac)                                  STEWARD90 */
    {
      WEST(111,35,58.24),
      NORTH(31,57,46.9),
      2071E0,
      "STEWARD90",
      "Steward 90 inch"
    },
    /* Russian 6m (1981 Almanac)                                       USSR6 */
    {
      EAST(41,26,30.0),
      NORTH(43,39,12.),
      2100E0,
      "USSR6",
      "USSR 6 metre"
    },
    /* Arecibo 1000' (1981 Almanac)                                  ARECIBO */
    {
      WEST(66,45,11.1),
      NORTH(18,20,36.6),
      496E0,
      "ARECIBO",
      "Arecibo 1000 foot"
    },
    /* Cambridge 5km (1981 Almanac)                                  CAMB5KM */
    {
      EAST(0,2,37.23),
      NORTH(52,10,12.2),
      17E0,
      "CAMB5KM",
      "Cambridge 5km"
    },
    /* Cambridge 1 mile (1981 Almanac)                             CAMB1MILE */
    {
      EAST(0,2,21.64),
      NORTH(52,9,47.3),
      17E0,
      "CAMB1MILE",
      "Cambridge 1 mile"
    },
    /* Bonn 100m (1981 Almanac)                                   EFFELSBERG */
    {
      EAST(6,53,1.5),
      NORTH(50,31,28.6),
      366E0,
      "EFFELSBERG",
      "Effelsberg 100 metre"
    },
    /* Greenbank 300' (1981 Almanac)                        GBVA300 (R.I.P.) */
    {
      WEST(79,50,56.36),
      NORTH(38,25,46.3),
      894E0,
      "(R.I.P.)",
      "Greenbank 300 foot"
    },
    /* Jodrell Bank Mk 1 (1981 Almanac)                             JODRELL1 */
    {
      WEST(2,18,25.),
      NORTH(53,14,10.5),
      78E0,
      "JODRELL1",
      "Jodrell Bank 250 foot"
    },
    /* Australia Telescope Parkes Observatory                         PARKES */
    /* (Peter te Lintel Hekkert) */
    {
      EAST(148,15,44.3591),
      SOUTH(32,59,59.8657),
      391.79E0,
      "PARKES",
      "Parkes 64 metre"
    },
    /* VLA (1981 Almanac)                                                VLA */
    {
      WEST(107,37,3.82),
      NORTH(34,4,43.5),
      2124E0,
      "VLA",
      "Very Large Array"
    },
    /* Sugar Grove 150' (1981 Almanac)                            SUGARGROVE */
    {
      WEST(79,16,23.),
      NORTH(38,31,14.),
      705E0,
      "SUGARGROVE",
      "Sugar Grove 150 foot"
    },
    /* Russian 600' (1981 Almanac)                                   USSR600 */
    {
      EAST(41,35,25.5),
      NORTH(43,49,32.),
      973E0,
      "USSR600",
      "USSR 600 foot"
    },
    /* Nobeyama 45 metre mm dish (based on 1981 Almanac entry)      NOBEYAMA */
    {
      EAST(138,29,12.),
      NORTH(35,56,19.),
      1350E0,
      "NOBEYAMA",
      "Nobeyama 45 metre"
    },
    /* James Clerk Maxwell 15 metre mm telescope, Mauna Kea             JCMT */
    /* From GPS measurements on 11Apr2007 for eSMA setup (R. Tilanus) */
    {
      WEST(155,28,37.30),
      NORTH(19,49,22.22),
      4124.75E0,
      "JCMT",
      "JCMT 15 metre"
    },
    /* ESO 3.5 metre NTT, La Silla (K.Wirenstrand)                    ESONTT */
    {
      WEST(70,43,7.),
      SOUTH(29,15,30.),
      2377E0,
      "ESONTT",
      "ESO 3.5 metre NTT"
    },
    /* St Andrews University Observatory (1982 Almanac)           ST.ANDREWS */
    {
      WEST(2,48,52.5),
      NORTH(56,20,12.),
      30E0,
      "ST.ANDREWS",
      "St Andrews"
    },
    /* Apache Point 3.5 metre (R.Owen)                                APO3.5 */
    {
      WEST(105,49,11.56),
      NORTH(32,46,48.96),
      2809E0,
      "APO3.5",
      "Apache Point 3.5m"
    },
    /* W.M.Keck Observatory, Telescope 1                               KECK1 */
    /* (William Lupton) */
    {
      WEST(155,28,28.99),
      NORTH(19,49,33.41),
      4160E0,
      "KECK1",
      "Keck 10m Telescope #1"
    },
    /* Tautenberg Schmidt (1983 Almanac)                            TAUTSCHM */
    {
      EAST(11,42,45.0),
      NORTH(50,58,51.0),
      331E0,
      "TAUTSCHM",
      "Tautenberg 1.34 metre Schmidt"
    },
    /* Palomar Schmidt (1981 Almanac)                              PALOMAR48 */
    {
      WEST(116,51,32.0),
      NORTH(33,21,26.0),
      1706E0,
      "PALOMAR48",
      "Palomar 48-inch Schmidt"
    },
    /* UK Schmidt, Siding Spring (1983 Almanac)                         UKST */
    {
      EAST(149,4,12.8),
      SOUTH(31,16,27.8),
      1145E0,
      "UKST",
      "UK 1.2 metre Schmidt, Siding Spring"
    },
    /* Kiso Schmidt, Japan (1981 Almanac)                               KISO */
    {
      EAST(137,37,42.2),
      NORTH(35,47,38.7),
      1130E0,
      "KISO",
      "Kiso 1.05 metre Schmidt, Japan"
    },
    /* ESO Schmidt, La Silla (1981 Almanac)                          ESOSCHM */
    {
      WEST(70,43,46.5),
      SOUTH(29,15,25.8),
      2347E0,
      "ESOSCHM",
      "ESO 1 metre Schmidt, La Silla"
    },
    /* Australia Telescope Compact Array                                ATCA */
    /* (WGS84 coordinates of Station 35, Mark Calabretta) */
    {
      EAST(149,33,0.500),
      SOUTH(30,18,46.385),
      236.9E0,
      "ATCA",
      "Australia Telescope Compact Array"
    },
    /* Australia Telescope Mopra Observatory                           MOPRA */
    /* (Peter te Lintel Hekkert) */
    {
      EAST(149,5,58.732),
      SOUTH(31,16,4.451),
      850E0,
      "MOPRA",
      "ATNF Mopra Observatory"
    },
    /* Subaru telescope, Mauna Kea                                     SUBARU */
    /* (IfA website, Richard Wainscoat) */
    {
      WEST(155,28,33.67),
      NORTH(19,49,31.81),
      4163E0,
      "SUBARU",
      "Subaru 8m telescope"
    },
    /* Canada-France-Hawaii Telescope, Mauna Kea                         CFHT */
    /* (IfA website, Richard Wainscoat) */
    {
      WEST(155,28,7.95),
      NORTH(19,49,30.91),
      4204.1E0,
      "CFHT",
      "Canada-France-Hawaii 3.6m Telescope"
    },
    /* W.M.Keck Observatory, Telescope 2                                KECK2 */
    /* (William Lupton) */
    {
      WEST(155,28,27.24),
      NORTH(19,49,35.62),
      4159.6E0,
      "KECK2",
      "Keck 10m Telescope #2"
    },
    /* Gemini North, Mauna Kea                                        GEMININ */
    /* (IfA website, Richard Wainscoat) */
    {
      WEST(155,28,8.57),
      NORTH(19,49,25.69),
      4213.4E0,
      "GEMININ",
      "Gemini North 8-m telescope"
    },
    /* Five College Radio Astronomy Observatory                        FCRAO */
    /* (Tim Jenness) */
    {
      WEST(72,20,42.0),
      NORTH(42,23,30.0),
      314E0,
      "FCRAO",
      "Five College Radio Astronomy Obs"
    },
    /* NASA Infra Red Telescope Facility                                IRTF */
    /* (IfA website, Richard Wainscoat) */
    {
      WEST(155,28,19.20),
      NORTH(19,49,34.39),
      4168.1E0,
      "IRTF",
      "NASA IR Telescope Facility, Mauna Kea"
    },
    /* Caltech Submillimeter Observatory                                 CSO */
    /* (IfA website, Richard Wainscoat; height estimated) */
    {
      WEST(155,28,31.79),
      NORTH(19,49,20.78),
      4080E0,
      "CSO",
      "Caltech Sub-mm Observatory, Mauna Kea"
    },
    /* ESO VLT, UT1                                                       VLT1 */
    /* (ESO website, VLT Whitebook Chapter 2) */
    {
      WEST(70,24,11.642),
      SOUTH(24,37,33.117),
      2635.43,
      "VLT1",
      "ESO VLT, Paranal, Chile: UT1"
    },
    /* ESO VLT, UT2                                                       VLT2 */
    /* (ESO website, VLT Whitebook Chapter 2) */
    {
      WEST(70,24,10.855),
      SOUTH(24,37,31.465),
      2635.43,
      "VLT2",
      "ESO VLT, Paranal, Chile: UT2"
    },
    /* ESO VLT, UT3                                                       VLT3 */
    /* (ESO website, VLT Whitebook Chapter 2) */
    {
      WEST(70,24,9.896),
      SOUTH(24,37,30.300),
      2635.43,
      "VLT3",
      "ESO VLT, Paranal, Chile: UT3"
    },
    /* ESO VLT, UT4                                                       VLT4 */
    /* (ESO website, VLT Whitebook Chapter 2) */
    {
      WEST(70,24,8.000),
      SOUTH(24,37,31.000),
      2635.43,
      "VLT4",
      "ESO VLT, Paranal, Chile: UT4"
    },
    /* Gemini South, Cerro Pachon                                     GEMINIS */
    /* (GPS readings by Patrick Wallace) */
    {
      WEST(70,44,11.5),
      SOUTH(30,14,26.7),
      2738E0,
      "GEMINIS",
      "Gemini South 8-m telescope"
    },
    /* Cologne Observatory for Submillimeter Astronomy (KOSMA)        KOSMA3M */
    /* (Holger Jakob) */
    {
      EAST(7,47,3.48),
      NORTH(45,58,59.772),
      3141E0,
      "KOSMA3M",
      "KOSMA 3m telescope, Gornergrat"
    },
    /* Magellan 1, 6.5m telescope at Las Campanas, Chile            MAGELLAN1 */
    /* (Skip Schaller) */
    {
      WEST(70,41,31.9),
      SOUTH(29,0,51.7),
      2408E0,
      "MAGELLAN1",
      "Magellan 1, 6.5m, Las Campanas"
    },
    /* Magellan 2, 6.5m telescope at Las Campanas, Chile            MAGELLAN2 */
    /* (Skip Schaller) */
    {
      WEST(70,41,33.5),
      SOUTH(29,0,50.3),
      2408E0,
      "MAGELLAN2",
      "Magellan 2, 6.5m, Las Campanas"
    },
    /* APEX - Atacama Pathfinder EXperiment, Llano de Chajnantor    APEX */
    /* (APEX web site) */
    {
      WEST(67,45,33.0),
      SOUTH(23,0,20.8),
      5105E0,
      "APEX",
      "APEX 12m telescope, Llano de Chajnantor"
    },
    /* NANTEN2 Submillimeter Observatory, 4m telescope Atacame desert NANTEN2 */
    /* (NANTEN2 web site) */
    {
      WEST(67,42,8.0),
      SOUTH(22,57,47.0),
      4865E0,
      "NANTEN2",
      "NANTEN2 4m telescope, Pampa la Bola"
    }
  };

  int retval = -1;    /* Return status. 0 if found. -1 if no match */

  /* Work out the number of telescopes */
  const size_t NTEL = sizeof(telData) / sizeof(struct telData);

  /* Prefill the return buffer in a pessimistic manner */
  star_strellcpy( name, "?", namelen );

  if (n > 0) {
    if (n <= NTEL) {
      /* Index into telData with correction for zero-based indexing */
      struct telData thistel;
      thistel = telData[n-1];
      *w = thistel.w;
      *p = thistel.p;
      *h = thistel.h;
      star_strellcpy( ident, thistel.shortname, identlen );
      star_strellcpy( name, thistel.longname, namelen );
      retval = 0;
    }

  } else {
    /* Searching */
    size_t i;
    for (i=0; i<NTEL; i++) {
      struct telData thistel = telData[i];
      if (strcasecmp( c, thistel.shortname) == 0) {
        /* a match */
        *w = thistel.w;
        *p = thistel.p;
        *h = thistel.h;
        star_strellcpy( ident, thistel.shortname, identlen );
        star_strellcpy( name, thistel.longname, namelen );
        retval = 0;
        break;
      }
    }

  }

  return retval;

}
Пример #7
0
void generateBlocked(MatGenFD mg, HYPRE_Int *rp, HYPRE_Int *cval, double *aval, Mat_dh A, Vec_dh b)
{
  START_FUNC_DH
  bool applyBdry = true;
  double *stencil = mg->stencil;
  HYPRE_Int id = mg->id;
  bool threeD = mg->threeD;
  HYPRE_Int px = mg->px, py = mg->py, pz = mg->pz; /* processor grid dimensions */
  HYPRE_Int p, q, r; /* this proc's position in processor grid */
  HYPRE_Int cc = mg->cc; /* local grid dimension (grid of unknowns) */
  HYPRE_Int nx = cc, ny = cc, nz = cc;
  HYPRE_Int lowerx, upperx, lowery, uppery, lowerz, upperz;
  HYPRE_Int startRow;
  HYPRE_Int x, y, z;
  bool debug = false;
  HYPRE_Int idx = 0, localRow = 0; /* nabor; */
  HYPRE_Int naborx1, naborx2, nabory1, nabory2, naborz1, naborz2;
  double *rhs;

  double hhalf = 0.5 * mg->hh;
  double bcx1 = mg->bcX1;
  double bcx2 = mg->bcX2;
  double bcy1 = mg->bcY1;
  double bcy2 = mg->bcY2;
  /* double bcz1 = mg->bcZ1; */
  /* double bcz2 = mg->bcZ2; */

  Vec_dhInit(b, A->m); CHECK_V_ERROR;
  rhs = b->vals;

  if (mg->debug && logFile != NULL) debug = true;
  if (! threeD) nz = 1;

  /* compute p,q,r from P,Q,R and myid */
  p = id % px;
  q = (( id - p)/px) % py;
  r = ( id - p - px*q)/( px*py );

  if (debug) {
    hypre_sprintf(msgBuf_dh, "this proc's position in subdomain grid: p= %i  q= %i  r= %i", p,q,r);
    SET_INFO(msgBuf_dh);
  }

   /* compute ilower and iupper from p,q,r and nx,ny,nz */
   /* zero-based */

   lowerx = nx*p;
   upperx = lowerx + nx;
   lowery = ny*q;
   uppery = lowery + ny;
   lowerz = nz*r;
   upperz = lowerz + nz;

  if (debug) {
    hypre_sprintf(msgBuf_dh, "local grid parameters: lowerx= %i  upperx= %i", lowerx, upperx);
    SET_INFO(msgBuf_dh);
    hypre_sprintf(msgBuf_dh, "local grid parameters: lowery= %i  uppery= %i", lowery, uppery);
    SET_INFO(msgBuf_dh);
    hypre_sprintf(msgBuf_dh, "local grid parameters: lowerz= %i  upperz= %i", lowerz, upperz);
    SET_INFO(msgBuf_dh);
  }

  startRow = mg->first;
  rp[0] = 0;

  for (z=lowerz; z<upperz; z++) {
    for (y=lowery; y<uppery; y++) {
      for (x=lowerx; x<upperx; x++) {

        if (debug) {
          hypre_fprintf(logFile, "row= %i  x= %i  y= %i  z= %i\n", localRow+startRow+1, x, y, z);
        }

        /* compute row values and rhs, at the current node */
        getstencil(mg,x,y,z);

        /* down plane */
        if (threeD) {
          if (z > 0) {
            naborz1 = rownum(threeD, x,y,z-1,nx,ny,nz,px,py);
            cval[idx]   = naborz1;
            aval[idx++] = FRONT(stencil);
          }
        }

        /* south */
        if (y > 0) {
          nabory1 = rownum(threeD, x,y-1,z,nx,ny,nz,px,py);
          cval[idx]   = nabory1;
          aval[idx++] = SOUTH(stencil);
        }

        /* west */
        if (x > 0) {
          naborx1 = rownum(threeD, x-1,y,z,nx,ny,nz,px,py);
          cval[idx]   = naborx1;
          aval[idx++] = WEST(stencil);
/*hypre_fprintf(logFile, "--- row: %i;  naborx1= %i\n", localRow+startRow+1, 1+naborx1);
*/
        }
/*
else {
hypre_fprintf(logFile, "--- row: %i;  x >= nx*px-1; naborx1 has old value: %i\n", localRow+startRow+1,1+naborx1);
}
*/

        /* center node */
        cval[idx]   = localRow+startRow;
        aval[idx++] = CENTER(stencil);


        /* east */
        if (x < nx*px-1) {
          naborx2 = rownum(threeD,x+1,y,z,nx,ny,nz,px,py);
          cval[idx]   = naborx2;
          aval[idx++] = EAST(stencil);
        }
/*
else {
hypre_fprintf(logFile, "--- row: %i;  x >= nx*px-1; nobors2 has old value: %i\n", localRow+startRow,1+naborx2);
}
*/

        /* north */
        if (y < ny*py-1) {
          nabory2 = rownum(threeD,x,y+1,z,nx,ny,nz,px,py);
          cval[idx]   = nabory2;
          aval[idx++] = NORTH(stencil);
        }

        /* up plane */
        if (threeD) {
          if (z < nz*pz-1) {
            naborz2 = rownum(threeD,x,y,z+1,nx,ny,nz,px,py);
            cval[idx]   = naborz2;
            aval[idx++] = BACK(stencil);
          }
        }

       /* rhs[rhsIdx++] = RHS(stencil); */
       rhs[localRow] = 0.0;

       ++localRow;
       rp[localRow] = idx; 

       /* apply boundary conditions; only for 2D! */
       if (!threeD && applyBdry) {
         HYPRE_Int globalRow = localRow+startRow-1;
         HYPRE_Int offset = rp[localRow-1];
         HYPRE_Int len = rp[localRow] - rp[localRow-1];
         double ctr, coeff;

/* hypre_fprintf(logFile, "globalRow = %i; naborx2 = %i\n", globalRow+1, naborx2+1); */

         if (x == 0) {         /* if x1 */
           coeff = mg->A(mg->a, x+hhalf,y,z);
           ctr   = mg->A(mg->a, x-hhalf,y,z);
           setBoundary_private(globalRow, cval+offset, aval+offset, len,
                               &(rhs[localRow-1]), bcx1, coeff, ctr, naborx2);
         } else if (x == nx*px-1) {  /* if x2 */
           coeff = mg->A(mg->a, x-hhalf,y,z);
           ctr   = mg->A(mg->a, x+hhalf,y,z);
           setBoundary_private(globalRow, cval+offset, aval+offset, len,
                               &(rhs[localRow-1]), bcx2, coeff, ctr, naborx1);
         } else if (y == 0) {  /* if y1 */
           coeff = mg->B(mg->b, x, y+hhalf,z);
           ctr   = mg->B(mg->b, x, y-hhalf,z);
           setBoundary_private(globalRow, cval+offset, aval+offset, len,
                               &(rhs[localRow-1]), bcy1, coeff, ctr, nabory2);
         } else if (y == ny*py-1) {        /* if y2 */
           coeff = mg->B(mg->b, x, y-hhalf,z);
           ctr   = mg->B(mg->b, x, y+hhalf,z);
           setBoundary_private(globalRow, cval+offset, aval+offset, len,
                               &(rhs[localRow-1]), bcy2, coeff, ctr, nabory1);
         } else if (threeD) {
           if (z == 0) {
             coeff = mg->B(mg->b, x, y, z+hhalf);
             ctr   = mg->B(mg->b, x, y, z-hhalf);
             setBoundary_private(globalRow, cval+offset, aval+offset, len,
                               &(rhs[localRow-1]), bcy1, coeff, ctr, naborz2);
           } else if (z == nz*nx-1) {
             coeff = mg->B(mg->b, x, y, z-hhalf);
             ctr   = mg->B(mg->b, x, y, z+hhalf);
             setBoundary_private(globalRow, cval+offset, aval+offset, len,
                               &(rhs[localRow-1]), bcy1, coeff, ctr, naborz1);
           }
         }
       }
      }
    }
  }
  END_FUNC_DH
}
Пример #8
0
void getstencil(MatGenFD g, HYPRE_Int ix, HYPRE_Int iy, HYPRE_Int iz)
{
  HYPRE_Int k; 
  double h = g->hh;
  double hhalf = h*0.5;
  double x = h*ix;
  double y = h*iy;
  double z = h*iz;
  double cntr = 0.0;
  double *stencil = g->stencil;
  double coeff;
  bool threeD = g->threeD;

  for (k=0; k<8; ++k) stencil[k] = 0.0;

  /* differentiation wrt x */
  coeff = g->A(g->a, x+hhalf,y,z);
  EAST(stencil) += coeff;
  cntr += coeff;

  coeff = g->A(g->a, x-hhalf,y,z);
  WEST(stencil) += coeff;
  cntr += coeff;

  coeff = g->D(g->d, x,y,z)*hhalf;
  EAST(stencil) += coeff;
  WEST(stencil) -= coeff;

  /* differentiation wrt y */
  coeff = g->B(g->b,x,y+hhalf,z);
  NORTH(stencil) += coeff;
  cntr += coeff;

  coeff = g->B(g->b,x,y-hhalf,z);
  SOUTH(stencil) += coeff;
  cntr += coeff;

  coeff = g->E(g->e,x,y,z)*hhalf;
  NORTH(stencil) += coeff;
  SOUTH(stencil) -= coeff;

  /* differentiation wrt z */
  if (threeD) {
    coeff = g->C(g->c,x,y,z+hhalf);
    BACK(stencil) += coeff;
    cntr += coeff;

    coeff = g->C(g->c,x,y,z-hhalf);
    FRONT(stencil) += coeff;
    cntr += coeff;

    coeff = g->F(g->f,x,y,z)*hhalf;
    BACK(stencil) += coeff;
    FRONT(stencil) -= coeff;
  }

  /* contribution from function G: */
  coeff = g->G(g->g,x,y,z);
  CENTER(stencil) = h*h*coeff - cntr;

  RHS(stencil) = h*h*g->H(g->h,x,y,z);
}
Пример #9
0
void generateStriped(MatGenFD mg, HYPRE_Int *rp, HYPRE_Int *cval, double *aval, Mat_dh A, Vec_dh b)
{
  START_FUNC_DH
  HYPRE_Int mGlobal;
  HYPRE_Int m = mg->m;
  HYPRE_Int beg_row, end_row;
  HYPRE_Int i, j, k, row;
  bool threeD = mg->threeD;
  HYPRE_Int idx = 0;
  double *stencil = mg->stencil;
  bool debug = false;
  HYPRE_Int plane, nodeRemainder;
  HYPRE_Int naborx1, naborx2, nabory1, nabory2;
  double *rhs;

  bool applyBdry = true;
  double hhalf;
  double bcx1 = mg->bcX1;
  double bcx2 = mg->bcX2;
  double bcy1 = mg->bcY1;
  double bcy2 = mg->bcY2;
  /* double bcz1 = mg->bcZ1; */
  /* double bcz2 = mg->bcZ2; */
  HYPRE_Int nx, ny;

  printf_dh("@@@ using striped partitioning\n");

  if (mg->debug && logFile != NULL) debug = true;

  /* recompute values (yuck!) */
  m = 9;
  Parser_dhReadInt(parser_dh,"-m", &m);  /* global grid dimension */
  mGlobal = m*m;                         /* global unkknowns */
  if (threeD) mGlobal *= m;
  i = mGlobal/mg->np;                    /* unknowns per processor */
  beg_row = i*mg->id;                    /* global number of 1st local row */
  end_row = beg_row + i;
  if (mg->id == mg->np-1) end_row = mGlobal;
  nx = ny = m;

  mg->hh = 1.0/(m-1);
  hhalf = 0.5 * mg->hh;

  A->n = m*m;
  A->m = end_row - beg_row;
  A->beg_row = beg_row;

  Vec_dhInit(b, A->m); CHECK_V_ERROR;
  rhs = b->vals;

  plane = m*m;

  if (debug) {
    hypre_fprintf(logFile, "generateStriped: beg_row= %i; end_row= %i; m= %i\n", beg_row+1, end_row+1, m);
  }

  for (row = beg_row; row<end_row; ++row) {
        HYPRE_Int localRow = row-beg_row;

        /* compute current node's position in grid */
        k = (row / plane);      
        nodeRemainder = row - (k*plane); /* map row to 1st plane */
        j = nodeRemainder / m;
        i = nodeRemainder % m;

        if (debug) {
          hypre_fprintf(logFile, "row= %i  x= %i  y= %i  z= %i\n", row+1, i,j,k);
        }

        /* compute column values and rhs entry for the current node */
        getstencil(mg,i,j,k);

        /* only homogenous Dirichlet boundary conditions presently supported */

        /* down plane */
        if (threeD) {
          if (k > 0) {
            cval[idx]   = row - plane;
            aval[idx++] = BACK(stencil);
          }
        }

        /* south */
        if (j > 0) {
          nabory1 = cval[idx] = row - m;
          aval[idx++] = SOUTH(stencil);
        }

        /* west */
        if (i > 0) {
          naborx1 = cval[idx] = row - 1;
          aval[idx++] = WEST(stencil);
        }

        /* center node */
        cval[idx]   = row;
        aval[idx++] = CENTER(stencil);

        /* east */
        if (i < m-1) {
          naborx2 = cval[idx] = row + 1;
          aval[idx++] = EAST(stencil);
        }

        /* north */
        if (j < m-1) {
          nabory2 = cval[idx] = row + m;
          aval[idx++] = NORTH(stencil);
        }

        /* up plane */
        if (threeD) {
          if (k < m-1) {
            cval[idx]   = row + plane;
            aval[idx++] = FRONT(stencil);
          }
        }
       rhs[localRow] = 0.0;
       ++localRow;
       rp[localRow] = idx; 

       /* apply boundary conditions; only for 2D! */
       if (!threeD && applyBdry) {
         HYPRE_Int offset = rp[localRow-1];
         HYPRE_Int len = rp[localRow] - rp[localRow-1];
         double ctr, coeff;

/* hypre_fprintf(logFile, "globalRow = %i; naborx2 = %i\n", row+1, row); */

         if (i == 0) {         /* if x1 */
           coeff = mg->A(mg->a, i+hhalf,j,k);
           ctr   = mg->A(mg->a, i-hhalf,j,k);
           setBoundary_private(row, cval+offset, aval+offset, len,
                               &(rhs[localRow-1]), bcx1, coeff, ctr, naborx2);
         } else if (i == nx-1) {  /* if x2 */
           coeff = mg->A(mg->a, i-hhalf,j,k);
           ctr   = mg->A(mg->a, i+hhalf,j,k);
           setBoundary_private(row, cval+offset, aval+offset, len,
                               &(rhs[localRow-1]), bcx2, coeff, ctr, naborx1);
         } else if (j == 0) {  /* if y1 */
           coeff = mg->B(mg->b, i, j+hhalf,k);
           ctr   = mg->B(mg->b, i, j-hhalf,k);
           setBoundary_private(row, cval+offset, aval+offset, len,
                               &(rhs[localRow-1]), bcy1, coeff, ctr, nabory2);
         } else if (j == ny-1) {        /* if y2 */
           coeff = mg->B(mg->b, i, j-hhalf,k);
           ctr   = mg->B(mg->b, i, j+hhalf,k);
           setBoundary_private(row, cval+offset, aval+offset, len,
                               &(rhs[localRow-1]), bcy2, coeff, ctr, nabory1);
         }
       }
  }
  END_FUNC_DH
}
Пример #10
0
/* 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;
}
Пример #11
0
/* Computes the active area for the current board position and the
 * read result that has just been stored in *entry.
 */
static void
compute_active_breakin_area(struct persistent_cache_entry *entry,
			    const char breakin_shadow[BOARDMAX], int dummy)
{
  int pos;
  int k, r;
  signed char active[BOARDMAX];
  int other = OTHER_COLOR(board[entry->apos]);
  UNUSED(dummy);

  /* We let the active area be
   * the string to connect +
   * the breakin shadow (which contains the goal) +
   * distance two expansion through empty intersections and own stones +
   * adjacent opponent strings +
   * liberties and neighbors of adjacent opponent strings with less than
   * five liberties +
   * liberties and neighbors of low liberty neighbors of adjacent opponent
   * strings with less than five liberties.
   */
  for (pos = BOARDMIN; pos < BOARDMAX; pos++)
    active[pos] = breakin_shadow[pos];

  signed_mark_string(entry->apos, active, 1);

  /* To be safe, also add the successful move. */
  if (entry->result != 0 && entry->move != 0)
    active[entry->move] = 1;

  /* Distance two expansion through empty intersections and own stones. */
  for (k = 1; k < 3; k++) {
    for (pos = BOARDMIN; pos < BOARDMAX; pos++) {
      if (!ON_BOARD(pos) || board[pos] == other || active[pos] != 0) 
	continue;
      if ((ON_BOARD(SOUTH(pos)) && active[SOUTH(pos)] == k)
	  || (ON_BOARD(WEST(pos)) && active[WEST(pos)] == k)
	  || (ON_BOARD(NORTH(pos)) && active[NORTH(pos)] == k)
	  || (ON_BOARD(EAST(pos)) && active[EAST(pos)] == k)) {
	if (board[pos] == EMPTY)
	  active[pos] = k + 1;
	else
	  signed_mark_string(pos, active, (signed char) (k + 1));
      }
    }
  }
  
  /* Adjacent opponent strings. */
  for (pos = BOARDMIN; pos < BOARDMAX; pos++) {
    if (board[pos] != other || active[pos] != 0) 
      continue;
    for (r = 0; r < 4; r++) {
      int pos2 = pos + delta[r];
      if (ON_BOARD(pos2)
	  && board[pos2] != other
	  && active[pos2] && active[pos2] <= 2) {
	signed_mark_string(pos, active, 1);
	break;
      }
    }
  }
  
  /* Liberties of adjacent opponent strings with less than four liberties +
   * liberties of low liberty neighbors of adjacent opponent strings
   * with less than five liberties.
   */
  for (pos = BOARDMIN; pos < BOARDMAX; pos++) {
    if (board[pos] == other && active[pos] > 0 && countlib(pos) < 4) {
      int libs[4];
      int liberties = findlib(pos, 3, libs);
      int adjs[MAXCHAIN];
      int adj;
      for (r = 0; r < liberties; r++)
	active[libs[r]] = 1;
      
      /* Also add liberties of neighbor strings if these are three
       * or less.
       */
      adj = chainlinks(pos, adjs);
      for (r = 0; r < adj; r++) {
	signed_mark_string(adjs[r], active, -1);
	if (countlib(adjs[r]) <= 3) {
	  int s;
	  int adjs2[MAXCHAIN];
	  int adj2;
	  liberties = findlib(adjs[r], 3, libs);
	  for (s = 0; s < liberties; s++)
	    active[libs[s]] = 1;
	  adj2 = chainlinks(pos, adjs2);
	  for (s = 0; s < adj2; s++)
	    signed_mark_string(adjs2[s], active, -1);
	}
      }
    }
  }
  
  for (pos = BOARDMIN; pos < BOARDMAX; pos++) {
    char value = board[pos];
    if (!ON_BOARD(pos))
      continue;
    if (!active[pos])
      value = GRAY;
    else if (IS_STONE(board[pos]) && countlib(pos) > 3 && active[pos] > 0)
      value |= HIGH_LIBERTY_BIT2;
    
    entry->board[pos] = value;
  }
}
Пример #12
0
static void
compute_active_reading_area(struct persistent_cache_entry *entry,
			    const char goal[BOARDMAX], int dummy)
{
  signed char active[BOARDMAX];
  int pos, r;
  UNUSED(dummy);

  /* Remains to set the board. We let the active area be the contested
   * string and reading shadow + adjacent empty and strings +
   * neighbors of active area so far + one more expansion from empty
   * to empty.
   */
  for (pos = BOARDMIN; pos < BOARDMAX; pos++)
    active[pos] = goal[pos];

  signed_mark_string(entry->apos, active, 1);

  /* To be safe, also add the successful move. */
  if (entry->result != 0 && entry->move != 0)
    active[entry->move] = 1;

  /* Add adjacent strings and empty. */
  for (pos = BOARDMIN; pos < BOARDMAX; pos++) {
    if (!ON_BOARD(pos))
      continue;
    if (active[pos] != 0) 
      continue;
    if ((ON_BOARD(SOUTH(pos)) && active[SOUTH(pos)] == 1)
	|| (ON_BOARD(WEST(pos)) && active[WEST(pos)] == 1)
	|| (ON_BOARD(NORTH(pos)) && active[NORTH(pos)] == 1)
	|| (ON_BOARD(EAST(pos)) && active[EAST(pos)] == 1)) {
      if (IS_STONE(board[pos]))
	signed_mark_string(pos, active, 2);
      else
	active[pos] = 2;
    }
  }

  /* Remove invincible strings. No point adding their liberties and
   * neighbors.
   */
  for (pos = BOARDMIN; pos < BOARDMAX; pos++) {
    if (!ON_BOARD(pos))
      continue;
    if (IS_STONE(board[pos]) && worm[pos].invincible)
      active[pos] = 0;
  }
  
  /* Expand empty to empty. */
  for (pos = BOARDMIN; pos < BOARDMAX; pos++) {
    if (IS_STONE(board[pos]) || active[pos] != 0) 
      continue;
    if ((board[SOUTH(pos)] == EMPTY && active[SOUTH(pos)] == 2)
	|| (board[WEST(pos)] == EMPTY && active[WEST(pos)] == 2)
	|| (board[NORTH(pos)] == EMPTY && active[NORTH(pos)] == 2)
	|| (board[EAST(pos)] == EMPTY && active[EAST(pos)] == 2))
      active[pos] = 3;
  }
  
  /* Add neighbors of active area so far. */
  for (pos = BOARDMIN; pos < BOARDMAX; pos++) {
    if (!ON_BOARD(pos))
      continue;
    if (active[pos] != 0) 
      continue;
    if ((ON_BOARD(SOUTH(pos)) && active[SOUTH(pos)] > 0
	 && active[SOUTH(pos)] < 4)
	|| (ON_BOARD(WEST(pos)) && active[WEST(pos)] > 0
	    && active[WEST(pos)] < 4)
	|| (ON_BOARD(NORTH(pos)) && active[NORTH(pos)] > 0
	    && active[NORTH(pos)] < 4)
	|| (ON_BOARD(EAST(pos)) && active[EAST(pos)] > 0
	    && active[EAST(pos)] < 4))
      active[pos] = 4;
  }

  /* Also add the previously played stones to the active area. */
  for (r = 0; r < stackp; r++)
    active[entry->stack[r]] = 5;

  for (pos = BOARDMIN; pos < BOARDMAX; pos++) {
    if (!ON_BOARD(pos))
      continue;
    entry->board[pos] = 
      active[pos] != 0 ? board[pos] : GRAY;
  }
}
Пример #13
0
static void compute_active_owl_area(struct persistent_cache_entry *entry,
				    const char goal[BOARDMAX],
				    int goal_color)
{
  int k, r;
  int pos;
  int other = OTHER_COLOR(goal_color);
  signed char active[BOARDMAX];

  /* We let the active area be the goal +
   * distance four expansion through empty intersections and own stones +
   * adjacent opponent strings +
   * liberties and neighbors of adjacent opponent strings with less than
   * five liberties +
   * liberties and neighbors of low liberty neighbors of adjacent opponent
   * strings with less than five liberties.
   */
  for (pos = BOARDMIN; pos < BOARDMAX; pos++)
    if (ON_BOARD(pos))
      active[pos] = (goal[pos] != 0);

  /* Also add critical moves to the active area. */
  if (ON_BOARD1(entry->move))
    active[entry->move] = 1;

  if (ON_BOARD1(entry->move2))
    active[entry->move2] = 1;

  /* Distance four expansion through empty intersections and own stones. */
  for (k = 1; k < 5; k++) {
    for (pos = BOARDMIN; pos < BOARDMAX; pos++) {
      if (!ON_BOARD(pos) || board[pos] == other || active[pos] > 0) 
	continue;
      if ((ON_BOARD(SOUTH(pos)) && active[SOUTH(pos)] == k)
	  || (ON_BOARD(WEST(pos)) && active[WEST(pos)] == k)
	  || (ON_BOARD(NORTH(pos)) && active[NORTH(pos)] == k)
	  || (ON_BOARD(EAST(pos)) && active[EAST(pos)] == k)) {
	if (board[pos] == EMPTY)
	  active[pos] = k + 1;
	else
	  signed_mark_string(pos, active, (signed char) (k + 1));
      }
    }
  }
  
  /* Adjacent opponent strings. */
  for (pos = BOARDMIN; pos < BOARDMAX; pos++) {
    if (board[pos] != other || active[pos] != 0) 
      continue;
    for (r = 0; r < 4; r++) {
      int pos2 = pos + delta[r];
      if (ON_BOARD(pos2) && board[pos2] != other && active[pos2] != 0) {
	signed_mark_string(pos, active, 1);
	break;
      }
    }
  }
  
  /* Liberties of adjacent opponent strings with less than five liberties +
   * liberties of low liberty neighbors of adjacent opponent strings
   * with less than five liberties.
   */
  for (pos = BOARDMIN; pos < BOARDMAX; pos++) {
    if (board[pos] == other && active[pos] > 0 && countlib(pos) < 5) {
      int libs[4];
      int liberties = findlib(pos, 4, libs);
      int adjs[MAXCHAIN];
      int adj;
      for (r = 0; r < liberties; r++)
	active[libs[r]] = 1;
      
      /* Also add liberties of neighbor strings if these are three
       * or less.
       */
      adj = chainlinks(pos, adjs);
      for (r = 0; r < adj; r++) {
	signed_mark_string(adjs[r], active, -1);
	if (countlib(adjs[r]) <= 3) {
	  int s;
	  int adjs2[MAXCHAIN];
	  int adj2;
	  liberties = findlib(adjs[r], 3, libs);
	  for (s = 0; s < liberties; s++)
	    active[libs[s]] = 1;
	  adj2 = chainlinks(pos, adjs2);
	  for (s = 0; s < adj2; s++)
	    signed_mark_string(adjs2[s], active, -1);
	}
      }
    }
  }
  
  for (pos = BOARDMIN; pos < BOARDMAX; pos++) {
    int value = board[pos];
    if (!ON_BOARD(pos))
      continue;
    if (!active[pos])
      value = GRAY;
    else if (IS_STONE(board[pos]) && countlib(pos) > 4 && active[pos] > 0)
      value |= HIGH_LIBERTY_BIT;
    
    entry->board[pos] = value;
  }
}
Пример #14
0
enum plugin_status
plugin_start (const void *parameter)
{
    int btn;

    rb->mkdir (DEFAULT_SAVE_DIR);

    global_setup ();

#ifdef GBN_TEST
    run_tests ();
    return PLUGIN_OK;
#endif

    if (!(parameter && load_game (parameter)))
    {
        if (parameter)
        {
            rb->splashf (2 * HZ, "Loading %s failed.", (char *) parameter);
        }

        if (!load_game (DEFAULT_SAVE))
        {
            rb->strcpy (save_file, DEFAULT_SAVE);

            if (!setup_game (MAX_BOARD_SIZE, MAX_BOARD_SIZE, 0, 0))
            {
                return PLUGIN_ERROR;
            }
        }
    }
    else
    {
        /* game loaded */
        if (rb->strcmp (save_file, DEFAULT_SAVE))
        {
            /* delete the scratch file if we loaded a game and it wasn't
             * from the scratch file
             */
            rb->remove (DEFAULT_SAVE);
        }
    }

    draw_screen_display ();

    autosave_counter = 0;
    for (;;)
    {
        btn = rb->button_get_w_tmo (HZ * 30);

        if (disable_shutdown)
        {
            /* tell rockbox we're not idle */
            rb->reset_poweroff_timer ();
        }

        bool is_idle = false;

        switch (btn)
        {

#if defined(GBN_BUTTON_NAV_MODE)
        case GBN_BUTTON_NAV_MODE:
        case GBN_BUTTON_NAV_MODE | BUTTON_REPEAT:
            if (nav_mode == NAV_MODE_TREE)
            {
                nav_mode = NAV_MODE_BOARD;
                rb->splash (2 * HZ / 3, "board navigation mode");
                draw_screen_display ();
            }
            else
            {
                nav_mode = NAV_MODE_TREE;
                rb->splash (2 * HZ / 3, "tree navigation mode");
                draw_screen_display ();
            }
            break;
#endif

#if defined(GBN_BUTTON_ADVANCE)
        case GBN_BUTTON_ADVANCE:
        case GBN_BUTTON_ADVANCE | BUTTON_REPEAT:
            if (has_more_nodes_sgf ())
            {
                if (!redo_node_sgf ())
                {
                    rb->splash (2 * HZ, "redo failed");
                }
                draw_screen_display ();
            }
            break;
#endif

#if defined(GBN_BUTTON_RETREAT)
        case GBN_BUTTON_RETREAT:
        case GBN_BUTTON_RETREAT | BUTTON_REPEAT:
            if (has_prev_nodes_sgf ())
            {
                if (!undo_node_sgf ())
                {
                    rb->splash (3 * HZ / 2, "Undo Failed");
                }
                draw_screen_display ();
            }
            break;
#endif

        case GBN_BUTTON_PLAY:
            if (play_mode == MODE_PLAY || play_mode == MODE_FORCE_PLAY)
            {
                if (!play_move_sgf (cursor_pos, current_player))
                {
                    rb->splash (HZ / 3, "Illegal Move");
                }
            }
            else if (play_mode == MODE_ADD_BLACK)
            {
                if (!add_stone_sgf (cursor_pos, BLACK))
                {
                    rb->splash (HZ / 3, "Illegal");
                }
            }
            else if (play_mode == MODE_ADD_WHITE)
            {
                if (!add_stone_sgf (cursor_pos, WHITE))
                {
                    rb->splash (HZ / 3, "Illegal");
                }
            }
            else if (play_mode == MODE_REMOVE)
            {
                if (!add_stone_sgf (cursor_pos, EMPTY))
                {
                    rb->splash (HZ / 3, "Illegal");
                }
            }
            else if (play_mode == MODE_MARK)
            {
                if (!add_mark_sgf (cursor_pos, PROP_MARK))
                {
                    rb->splash (HZ / 3, "Couldn't Mark");
                }
            }
            else if (play_mode == MODE_CIRCLE)
            {
                if (!add_mark_sgf (cursor_pos, PROP_CIRCLE))
                {
                    rb->splash (HZ / 3, "Couldn't Mark");
                }
            }
            else if (play_mode == MODE_SQUARE)
            {
                if (!add_mark_sgf (cursor_pos, PROP_SQUARE))
                {
                    rb->splash (HZ / 3, "Couldn't Mark");
                }
            }
            else if (play_mode == MODE_TRIANGLE)
            {
                if (!add_mark_sgf (cursor_pos, PROP_TRIANGLE))
                {
                    rb->splash (HZ / 3, "Couldn't Mark");
                }
            }
            else if (play_mode == MODE_LABEL)
            {
                if (!add_mark_sgf (cursor_pos, PROP_LABEL))
                {
                    rb->splash (HZ / 3, "Couldn't Label");
                }
            }
            else
            {
                rb->splash (HZ, "mode not implemented");
            }

            draw_screen_display ();
            break;

        case GBN_BUTTON_RIGHT:
        case GBN_BUTTON_RIGHT | BUTTON_REPEAT:
#if defined(GBN_BUTTON_NAV_MODE)
            if (nav_mode == NAV_MODE_TREE)
            {
                if (has_more_nodes_sgf ())
                {
                    if (!redo_node_sgf ())
                    {
                        rb->splash (2 * HZ, "Redo Failed");
                    }
                    draw_screen_display ();
                }
            }
            else
            {
#endif
                cursor_pos = WRAP (EAST (cursor_pos));
                draw_screen_display ();
#if defined(GBN_BUTTON_NAV_MODE)
            }
#endif
            break;

        case GBN_BUTTON_LEFT:
        case GBN_BUTTON_LEFT | BUTTON_REPEAT:
#if defined(GBN_BUTTON_NAV_MODE)
            if (nav_mode == NAV_MODE_TREE)
            {
                if (has_prev_nodes_sgf ())
                {
                    if (!undo_node_sgf ())
                    {
                        rb->splash (2 * HZ, "Undo Failed");
                    }
                    draw_screen_display ();
                }
            }
            else
            {
#endif
                cursor_pos = WRAP (WEST (cursor_pos));
                draw_screen_display ();
#if defined(GBN_BUTTON_NAV_MODE)
            }
#endif
            break;

        case GBN_BUTTON_DOWN:
        case GBN_BUTTON_DOWN | BUTTON_REPEAT:
            cursor_pos = WRAP (SOUTH (cursor_pos));
            draw_screen_display ();
            break;

        case GBN_BUTTON_UP:
        case GBN_BUTTON_UP | BUTTON_REPEAT:
            cursor_pos = WRAP (NORTH (cursor_pos));
            draw_screen_display ();
            break;

        case GBN_BUTTON_MENU:
            if (do_main_menu ())
            {
                save_game (DEFAULT_SAVE);

                global_cleanup ();
                return PLUGIN_OK;
            }

            draw_screen_display ();
            break;

#if defined(GBN_BUTTON_CONTEXT)
        case GBN_BUTTON_CONTEXT:
            do_context_menu ();
            draw_screen_display ();
            break;
#endif

#if defined(GBN_BUTTON_NEXT_VAR)
        case GBN_BUTTON_NEXT_VAR:
        case GBN_BUTTON_NEXT_VAR | BUTTON_REPEAT:
        {
            int temp;
            if ((temp = next_variation_sgf ()) >= 0)
            {
                draw_screen_display ();
                rb->splashf (2 * HZ / 3, "%d of %d", temp,
                             num_variations_sgf ());
                draw_screen_display ();
            }
            else
            {
                if (num_variations_sgf () > 1)
                {
                    rb->splashf (HZ, "Error %d in next_variation_sgf", temp);
                }
                draw_screen_display ();
            }
            break;
        }
#endif

        case BUTTON_NONE:
            is_idle = true;
        default:
            if (rb->default_event_handler (btn) == SYS_USB_CONNECTED)
            {
                return PLUGIN_USB_CONNECTED;
            }
            break;
        };

        if (is_idle && autosave_dirty)
        {
            ++autosave_counter;

            if (autosave_time != 0 &&
                autosave_counter / 2 >= autosave_time)
                /* counter is in 30 second increments, autosave_time is in
                 * minutes
                 */
            {
                DEBUGF("autosaving\n");
                rb->splash(HZ / 4, "Autosaving...");
                save_game(DEFAULT_SAVE);
                draw_screen_display();
                autosave_counter = 0;
            }
        }
        else
        {
            autosave_counter = 0;
        }
    }

    return PLUGIN_OK;
}
void
store_persistent_owl_cache(int routine, int apos, int bpos, int cpos,
			   int result, int move, int move2, int certain,
			   int tactical_nodes,
			   char goal[BOARDMAX], int goal_color)
{
  char active[BOARDMAX];
  int pos;
  int k;
  int r;
  int other = OTHER_COLOR(goal_color);
  gg_assert(stackp == 0);

  /* If cache is full, first try to purge it. */
  if (persistent_owl_cache_size == MAX_OWL_CACHE_SIZE)
    purge_persistent_owl_cache();

  /* FIXME: Kick out oldest or least expensive entry instead of giving up. */
  if (persistent_owl_cache_size == MAX_OWL_CACHE_SIZE) {
    TRACE_OWL_PERFORMANCE("Persistent owl cache full.\n");
    return;
  }

  persistent_owl_cache[persistent_owl_cache_size].boardsize  	 = board_size;
  persistent_owl_cache[persistent_owl_cache_size].routine    	 = routine;
  persistent_owl_cache[persistent_owl_cache_size].apos	     	 = apos;
  persistent_owl_cache[persistent_owl_cache_size].bpos	     	 = bpos;
  persistent_owl_cache[persistent_owl_cache_size].cpos	     	 = cpos;
  persistent_owl_cache[persistent_owl_cache_size].result     	 = result;
  persistent_owl_cache[persistent_owl_cache_size].result_certain = certain;
  persistent_owl_cache[persistent_owl_cache_size].move	         = move;
  persistent_owl_cache[persistent_owl_cache_size].move2	         = move2;
  persistent_owl_cache[persistent_owl_cache_size].tactical_nodes =
    tactical_nodes;
  persistent_owl_cache[persistent_owl_cache_size].movenum = movenum;
  
  /* Remains to set the board. We let the active area be
   * the goal +
   * distance four expansion through empty intersections and own stones +
   * adjacent opponent strings +
   * liberties of adjacent opponent strings with less than five liberties +
   * liberties of low liberty neighbors of adjacent opponent strings
   * with less than five liberties.
   */
  for (pos = BOARDMIN; pos < BOARDMAX; pos++)
    if (ON_BOARD(pos))
      active[pos] = (goal[pos] != 0);

  /* Also add critical moves to the active area. */
  if (ON_BOARD1(move))
    active[move] = 1;

  if (ON_BOARD1(move2))
    active[move2] = 1;

  /* Distance four expansion through empty intersections and own stones. */
  for (k = 1; k < 5; k++) {
    for (pos = BOARDMIN; pos < BOARDMAX; pos++){
      if (!ON_BOARD(pos) || board[pos] == other || active[pos] != 0) 
	continue;
      if ((ON_BOARD(SOUTH(pos)) && active[SOUTH(pos)] == k)
	  || (ON_BOARD(WEST(pos)) && active[WEST(pos)] == k)
	  || (ON_BOARD(NORTH(pos)) && active[NORTH(pos)] == k)
	  || (ON_BOARD(EAST(pos)) && active[EAST(pos)] == k)) {
	if (board[pos] == EMPTY)
	  active[pos] = k + 1;
	else
	  mark_string(pos, active, (char) (k + 1));
      }
    }
  }
  
  /* Adjacent opponent strings. */
  for (pos = BOARDMIN; pos < BOARDMAX; pos++) {
    if (board[pos] != other || active[pos] != 0) 
      continue;
    for (r = 0; r < 4; r++) {
      int pos2 = pos + delta[r];
      if (ON_BOARD(pos2) && board[pos2] != other && active[pos2] != 0) {
	mark_string(pos, active, (char) 1);
	break;
      }
    }
  }
  
  /* Liberties of adjacent opponent strings with less than five liberties +
   * liberties of low liberty neighbors of adjacent opponent strings
   * with less than five liberties.
   */
  for (pos = BOARDMIN; pos < BOARDMAX; pos++) {
    if (board[pos] == other && active[pos] != 0 && countlib(pos) < 5) {
      int libs[4];
      int liberties = findlib(pos, 4, libs);
      int adjs[MAXCHAIN];
      int adj;
      for (r = 0; r < liberties; r++)
	active[libs[r]] = 1;
      
      /* Also add liberties of neighbor strings if these are three
       * or less.
       */
      adj = chainlinks(pos, adjs);
      for (r = 0; r < adj; r++) {
	if (countlib(adjs[r]) <= 3) {
	  int s;
	  liberties = findlib(adjs[r], 3, libs);
	  for (s = 0; s < liberties; s++)
	    active[libs[s]] = 1;
	}
      }
    }
  }
  
  for (pos = BOARDMIN; pos < BOARDMAX; pos++) {
    int value = board[pos];
    if (!ON_BOARD(pos))
      continue;
    if (!active[pos])
      value = GRAY;
    else if (IS_STONE(board[pos]) && countlib(pos) > 4)
      value |= HIGH_LIBERTY_BIT;
    
    persistent_owl_cache[persistent_owl_cache_size].board[pos] = value;
  }

  if (debug & DEBUG_OWL_PERSISTENT_CACHE) {
    gprintf("%o Stored result in cache (entry %d):\n",
	    persistent_owl_cache_size);
    print_persistent_owl_cache_entry(persistent_owl_cache_size);
  }
  
  persistent_owl_cache_size++;
}
/* Store a new read result in the persistent cache. */
void
store_persistent_reading_cache(int routine, int str, int result, int move,
			       int nodes)
{
  char active[BOARDMAX];
  int k;
  int r;
  int score = nodes;
  struct reading_cache *entry;

  ASSERT1(result == 0 || (move == 0) || ON_BOARD(move), move);

  /* Never cache results at too great depth. */
  if (stackp > MAX_READING_CACHE_DEPTH)
    return;

  /* If cache is still full, consider kicking out an old entry. */
  if (persistent_reading_cache_size == MAX_READING_CACHE_SIZE) {
    int worst_entry = -1;
    int worst_score = score;
    
    for (k = 1; k < persistent_reading_cache_size; k++) {
      if (persistent_reading_cache[k].score < worst_score) {
	worst_score = persistent_reading_cache[k].score;
	worst_entry = k;
      }
    }

    if (worst_entry != -1) {
      /* Move the last entry in the cache here to make space.
       */
      if (worst_entry < persistent_reading_cache_size - 1)
	persistent_reading_cache[worst_entry] 
	  = persistent_reading_cache[persistent_reading_cache_size - 1];
      persistent_reading_cache_size--;
    }
    else
      return;
  }

  entry = &(persistent_reading_cache[persistent_reading_cache_size]);
  entry->boardsize       = board_size;
  entry->movenum         = movenum;
  entry->nodes           = nodes;
  entry->score           = score;
  entry->remaining_depth = depth - stackp;
  entry->routine         = routine;
  entry->str	         = str;
  entry->result          = result;
  entry->move	         = move;

  for (r = 0; r < MAX_READING_CACHE_DEPTH; r++) {
    if (r < stackp)
      get_move_from_stack(r, &(entry->stack[r]), &(entry->move_color[r]));
    else {
      entry->stack[r] = 0;
      entry->move_color[r] = EMPTY;
    }
  }
  
  /* Remains to set the board. We let the active area be the contested
   * string and reading shadow + adjacent empty and strings +
   * neighbors of active area so far + one more expansion from empty
   * to empty.
   */
  for (k = BOARDMIN; k < BOARDMAX; k++)
    active[k] = shadow[k];

  mark_string(str, active, 1);

  /* To be safe, also add the successful move. */
  if (result != 0 && move != 0)
    active[move] = 1;

  /* Add adjacent strings and empty. */
  for (k = BOARDMIN; k < BOARDMAX; k++) {
    if (!ON_BOARD(k))
      continue;
    if (active[k] != 0) 
      continue;
    if ((ON_BOARD(SOUTH(k)) && active[SOUTH(k)] == 1)
	|| (ON_BOARD(WEST(k)) && active[WEST(k)] == 1)
	|| (ON_BOARD(NORTH(k)) && active[NORTH(k)] == 1)
	|| (ON_BOARD(EAST(k)) && active[EAST(k)] == 1)) {
      if (IS_STONE(board[k]))
	mark_string(k, active, 2);
      else
	active[k] = 2;
    }
  }

  /* Remove invincible strings. No point adding their liberties and
   * neighbors.
   */
  for (k = BOARDMIN; k < BOARDMAX; k++) {
    if (!ON_BOARD(k))
      continue;
    if (IS_STONE(board[k]) && worm[k].invincible)
      active[k] = 0;
  }
  
  /* Expand empty to empty. */
  for (k = BOARDMIN; k < BOARDMAX; k++) {
    if (IS_STONE(board[k]) || active[k] != 0) 
      continue;
    if ((board[SOUTH(k)] == EMPTY && active[SOUTH(k)] == 2)
	|| (board[WEST(k)] == EMPTY && active[WEST(k)] == 2)
	|| (board[NORTH(k)] == EMPTY && active[NORTH(k)] == 2)
	|| (board[EAST(k)] == EMPTY && active[EAST(k)] == 2))
      active[k] = 3;
  }
  
  /* Add neighbors of active area so far. */
  for (k = BOARDMIN; k < BOARDMAX; k++) {
    if (!ON_BOARD(k))
      continue;
    if (active[k] != 0) 
      continue;
    if ((ON_BOARD(SOUTH(k)) && active[SOUTH(k)] > 0 && active[SOUTH(k)] < 4)
	|| (ON_BOARD(WEST(k)) && active[WEST(k)] > 0 && active[WEST(k)] < 4)
	|| (ON_BOARD(NORTH(k)) && active[NORTH(k)] > 0 && active[NORTH(k)] < 4)
	|| (ON_BOARD(EAST(k)) && active[EAST(k)] > 0 && active[EAST(k)] < 4))
      active[k] = 4;
  }

  /* Also add the previously played stones to the active area. */
  for (r = 0; r < stackp; r++)
    active[entry->stack[r]] = 5;

  for (k = BOARDMIN; k < BOARDMAX; k++) {
    if (!ON_BOARD(k))
      continue;
    entry->board[k] = 
      active[k] != 0 ? board[k] : GRAY;
  }

  if (0) {
    gprintf("%o Stored result in cache (entry %d):\n",
	    persistent_reading_cache_size);
    print_persistent_reading_cache_entry(persistent_reading_cache_size);
    gprintf("%o Reading shadow was:\n");
    draw_reading_shadow();
  }
  
  persistent_reading_cache_size++;
}